一、背景

使用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. IDEA好用插件推荐

    Maven Helper:排查maven依赖冲突神器,强力推荐! Alibaba Java Coding Guidelines:阿里巴巴编程规范 CamelCase:驼峰命名工具,SHIFT + AL ...

  2. 基于thymeleaf实现简单登录

    1.引入thymeleaf.静态资源等依赖 <dependency> <groupId>org.springframework.boot</groupId> < ...

  3. 动态script标签同步加载 ps:无打包编译,静态实现静态资源入口动态配置,无编译打包静态资源添加版本号

    /**功能:创建动态标签加载css ,js文件,重点是js文件,利用onloading加递归实现动态标签的同步加载用法:在html文件body底部script内部声明并调用下列函数,obj中写要加载的 ...

  4. JQuery 文本框控制验证数字

    $("input[name=XUEFEN]").keypress(function(event) { var keyCode = event.which; if (keyCode ...

  5. 3_PHP表达式_5_数据类型转换_类型自动转换

    以下为学习孔祥盛主编的<PHP编程基础与实例教程>(第二版)所做的笔记. PHP类型转换分为类型自动转换和类型强制转换. 1.布尔型数据参与算数运算时,TRUE被转换为整数1,FALSE被 ...

  6. Flask基础原理

    一.Flask简介 Flask是一个基于Python开发并且依赖jinja2模板和Werkzeug WSGI服务的一个微型框架. Werkzeug的本质是Socket服务端,用于接收http请求并对请 ...

  7. Ubuntu下编译 Hadoop2.9

    Ubuntu 下编译 Hadoop-2.9.2 系统环境 系统: ubuntu-18.10-desktop-amd64 maven: Apache Maven 3.6.0 jdk: jdk_1.8.0 ...

  8. elementUI图片墙上传

    elementUI提供了照片墙上传的功能,我们直接拿来用. 以下是实现代码: <template> <div style="padding: 50px;"> ...

  9. jq的ajax方法

    相较与js异步对象的繁琐,jq的ajax对象结构更加清晰 一:ajax对象简述 ajax(Asynchronous JavaScript and XML),异步的xml和js对象,主要用于在不刷新全局 ...

  10. String.getBytes()方法中的中文编码问题

    得到一个操作系统默认的编码格式的字节数组.这表示在不同的操作系统下,返回的东西不一样! byte[] a= "中".getBytes() String.getBytes(Strin ...