一、背景

使用typedef或者using定义类型别名是非常常见的手段,在c++里面,有时为了封装性,模块性等原因还会在某一个namespace或者class内部定义类型别名。

最近在写c++代码的时候,有实现一个模板类,说实话,虽然用c++用了好多年了,但还真没花多少时间去研究模板,因为我始终觉得,做项目,开发软件,不是为了炫技,我也不认为会玩儿模板就是牛人大神了,最主要的是把握好三个“用”就好了,这三个用分别是:实用,适用,够用。

言归正传,这次实用模板类,也会用到在模板类里面实用typedef定义类型别名,当然,其实这也没什么问题,只要在模板类内部实现每个模板类的成员函数,这一切都不是问题,但是我想让模板类声明的干净纯粹一些,换句话说,就是将模板类的成员函数全部都放到外面去实现,而不是直接的模板类内部实现,模板类只是声明每个成员函数。

问题就来了,欲知详情,请接着往下看。

二、解决方案

假定这里需要实现这样一个模板myFoo, 这个模板有一个成员函数size,这个成员函数会返回一个表示size的值,其声明大致如下:

template<typename T>
class myFoo
{
public:
using size_type = std::size_t; public:
size_type size() const;
private:
size_type mSize = 5;
};

  这里如果只是在模板类里面实现这个size成员函数的话,就不会有任何问题,形如:

template<typename T>
class myFoo
{
public:
using size_type = std::size_t; public:
size_type size() const
{
return mSize;
}
private:
size_type mSize = 5;
};

  问题就是,我需要在模板类外面实现这个size成员函数,如果按照普通的c++类来实现,如下所示:

myFoo::size_type myFoo::size() const
{
return mSize;
}

  当然,如果你这样写的话,编译器是不会让你过去的,模板类的成员函数在类外面实现自有它的规则,形如:

template<typename T>
myFoo<T>::size_type myFoo<T>::size() const
{
return mSize;
}

  

当然,如果你走到这一步了,说明你对模板类外实现成员函数的规则算是基本了解了,但这种写法仍然不对,也会被编译器无情的拒绝。

因为,对于模板类,myFoo::size_type 这样的写法,会被认为是引用一个名为size_type的成员变量,而这里的size_type只是size_t的别名而已,是个类型名,而不是成员变量。

答案最终揭晓,正确的写法是,保证对size_type的引用是类型名的引用,其实解决方法并不止一种:

  • 其一:使用typename关键字,具体代码如下:
template<typename T>
typename myFoo<T>::size_type myFoo<T>::size() const
{
return mSize;
}
  • 其二:使用auto和decltype关键字,具体代码如下(包含模板类的声明部分):
template<typename T>
class myFoo
{
public:
using size_type = std::size_t; public:
auto size() const -> decltype(myFoo<T>::mSize);
private:
size_type mSize = 5;
}; /** size成员函数实现 */
template<typename T>
auto myFoo<T>::size() const -> decltype(myFoo<T>::mSize)
{
return mSize;
}

  

两种写法都可,选哪一种就看个人习惯和爱好了(其实:在visual studio2017里面只需要auto关键字就可以了,不需要decltype关键之,后面会说到)。

三、模板函数的返回值类型推导

这里顺便说一下,模板函数的返回值类型推导, 举一个简单的例子,实现一个将两个变量相加的模板函数,原型大致为:

template<typename T1, typename T2>
xxx myAdd(T1 a, T2 b);

  

之所以这里的返回值,是一个“xxx”, 是因为无法确定这个返回值类型会是什么,如果T1和T2都是相同类型如int,那么还好办,“xxx”取T1或者T2都可以,但如果T1和T2类型不同,比如一个是int,一个double,理论上,应该返回double类型, 但是这个模板函数并不知道T1是double,还是T2是double。

这里你可以要说,这简单啊,直接用auto关键字就好了,这里需要说明一下,我试验的结果是,使用visual studio2017的话,用auto关键字作为这个模板函数的返回值是可以的,其形式如下:

template<typename T1, typename T2>
auto myAdd(T1 a, T2 b)
{
return a + b;
}

  但是如果使用gcc的话,这样写就不行,会报如下的错误:

‘myAdd’ function uses ‘auto’ type specifier without trailing return type

  也就是说,需要说明模板函数返回值的推导方式,根据c++11标准,这种情况需要在模板函数后面用decltype关键字添加返回值类型的推导,代码如下:

template<typename T1, typename T2>
auto myAdd(T1 a, T2 b) -> decltype(a + b)
{
return a + b;
}

  

这种写法在visual studio2017和gcc中都能编译通过,这里不太清楚,是否visual studio 2017对只使用auto关键字的情况作了某些默认的推导。

所以为了代码的通用性,还是使用后面这种写法吧,这样在使用不同编译器时就不需要针对特定的编译器进行代码修改了。

[转]引用模板类中定义的类型(用typedef或using)以及auto、decltype、typename的使用的更多相关文章

  1. 基类中定义的虚函数在派生类中重新定义时,其函数原型,包括返回类型、函数名、参数个数、参数类型及参数的先后顺序,都必须与基类中的原型完全相同 but------> 可以返回派生类对象的引用或指针

      您查询的关键词是:c++primer习题15.25 以下是该网页在北京时间 2016年07月15日 02:57:08 的快照: 如果打开速度慢,可以尝试快速版:如果想更新或删除快照,可以投诉快照. ...

  2. C++模板类中使用静态成员变量(例如Singleton模式)

    一个最简单Singleton的例子: ///////// Test.h /////////template <class _T>class CTest{private:_T n;stati ...

  3. gcc的bug? c++模板类中友元函数的訪问权限问题

    原文地址:http://stackoverflow.com/q/23171337/3309790 在c++中,模板类中能够直接定义一个友元函数.该函数拥有訪问该模板类非public成员的权限. 比方: ...

  4. C++模板类中友元函数的写法

    首先,已声明好的类Triangle file://Triangle.h template<class T> class Triangle{ public: Triangle(T width ...

  5. MVC中子页面如何引用模板页中的jquery脚本

    MVC中子页面如何引用模板页中的jquery脚本 最近在学习mvc,遇到了一个问题:在html页面中写js代码,都是引用mvc5自带的jquery脚本,虽然一拖(将指定的jquery脚本如 jquer ...

  6. MFC 如何在一个类中使用在其他类中定义的变量或函数

    [声明:本文的知识点来源于网络,参考网址:https://blog.csdn.net/bill_ming/article/details/7407848] [以下三种方法亲测有效,可以根据具体情况来选 ...

  7. ES6 class类中定义私有变量

    ES6 class类中定义私有变量 class类的不足 看起来, es6 中 class 的出现拉近了 JS 和传统 OOP 语言的距离.但是,它仅仅是一个语法糖罢了,不能实现传统 OOP 语言一样的 ...

  8. 泛型方法或泛型类中的方法是内部调用、PInvoke 或是在 COM 导入类中定义的。

    泛型基类中引用Api函数定义时static extern,在子类中会提示: 未处理TypeLoadException 泛型方法或泛型类中的方法是内部调用.PInvoke 或是在 COM 导入类中定义的 ...

  9. java类中定义接口

    今天看到一个java类中定义了接口,写个备忘录,记录一下 package com.gxf.test; public class Test_interface { public interface sh ...

随机推荐

  1. Vue组件全局/局部注册

    全局注册 main.js中创建 Vue.component('button-counter', { data: function () { return { count: 0 } }, templat ...

  2. VBA子程序(十六)

    子程序(Sub Procedures,也叫子过程)与函数类似,但有一些差异. 子过程不需要有返回一个值,而函数可能会或可能不会有返回一个值. 子程序可以不用call关键字来调用. 子程序总是包含在Su ...

  3. Django-cms show_menu参数解释

    当页面结构设置(/admin/cms/page)如下: - Home (level=0) - About Us (level=1) - About Company Services (level=2) ...

  4. vue中修改第三方组件的样式并不造成污染

    vue引用了第三方组件, 需要在组件中局部修改第三方组件的样式, 而又不想去除scoped属性造成组件之间的样式污染. 此时只能通过>>>,穿透scoped. 但是,在sass中存在 ...

  5. Django中ORM多对多表的操作

    自己创建第三张表建立多对多关系 表的创建 # 老师表和学生表可以是一个多对多的关系,建表时可以手动建立第三张表建立关联 class Student(models.Model): name = mode ...

  6. Authorization Bypass in RSA NetWitness

    https://www.cnblogs.com/iAmSoScArEd/ SEC Consult Vulnerability Lab Security Advisory < 20190515-0 ...

  7. python 将字符串中的unicode字符码转换成字符

    将字符串str =’\u98ce\u534e\u7684\u51b2\u950b'转换成汉字显示 可以直接print输出 print u'\u98ce\u534e\u7684\u51b2\u950b' ...

  8. python识别文字tesseract

    Ubuntu版本: .tesseract-ocr安装 sudo apt-get install tesseract-ocr .pytesseract安装 sudo pip install pytess ...

  9. Linux日志查看

    Linux日志查看: 1.Last -a 把从何处登入系统的主机名称或IP地址,显示在最后一行.-d 指定记录文件.指定记录文件.将IP地址转换成主机名称.-f <记录文件>  指定记录文 ...

  10. http接口测试工具-Advanced-REST-client

    非常好用的http接口测试工具 相信作为一个java开发人员,大家或多或少的要写或者接触一些http接口.而当我们需要本地调试接口常常会因为没有一款好用的工具而烦恼.今天要给大家介绍一款非常好用.实用 ...