[转]引用模板类中定义的类型(用typedef或using)以及auto、decltype、typename的使用
一、背景
使用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的使用的更多相关文章
- 基类中定义的虚函数在派生类中重新定义时,其函数原型,包括返回类型、函数名、参数个数、参数类型及参数的先后顺序,都必须与基类中的原型完全相同 but------> 可以返回派生类对象的引用或指针
您查询的关键词是:c++primer习题15.25 以下是该网页在北京时间 2016年07月15日 02:57:08 的快照: 如果打开速度慢,可以尝试快速版:如果想更新或删除快照,可以投诉快照. ...
- C++模板类中使用静态成员变量(例如Singleton模式)
一个最简单Singleton的例子: ///////// Test.h /////////template <class _T>class CTest{private:_T n;stati ...
- gcc的bug? c++模板类中友元函数的訪问权限问题
原文地址:http://stackoverflow.com/q/23171337/3309790 在c++中,模板类中能够直接定义一个友元函数.该函数拥有訪问该模板类非public成员的权限. 比方: ...
- C++模板类中友元函数的写法
首先,已声明好的类Triangle file://Triangle.h template<class T> class Triangle{ public: Triangle(T width ...
- MVC中子页面如何引用模板页中的jquery脚本
MVC中子页面如何引用模板页中的jquery脚本 最近在学习mvc,遇到了一个问题:在html页面中写js代码,都是引用mvc5自带的jquery脚本,虽然一拖(将指定的jquery脚本如 jquer ...
- MFC 如何在一个类中使用在其他类中定义的变量或函数
[声明:本文的知识点来源于网络,参考网址:https://blog.csdn.net/bill_ming/article/details/7407848] [以下三种方法亲测有效,可以根据具体情况来选 ...
- ES6 class类中定义私有变量
ES6 class类中定义私有变量 class类的不足 看起来, es6 中 class 的出现拉近了 JS 和传统 OOP 语言的距离.但是,它仅仅是一个语法糖罢了,不能实现传统 OOP 语言一样的 ...
- 泛型方法或泛型类中的方法是内部调用、PInvoke 或是在 COM 导入类中定义的。
泛型基类中引用Api函数定义时static extern,在子类中会提示: 未处理TypeLoadException 泛型方法或泛型类中的方法是内部调用.PInvoke 或是在 COM 导入类中定义的 ...
- java类中定义接口
今天看到一个java类中定义了接口,写个备忘录,记录一下 package com.gxf.test; public class Test_interface { public interface sh ...
随机推荐
- Spring Boot集成Mybatis完整实例
步骤: 添加Mybatis依赖: 添加数据库依赖: 配置属性文件: (具体的属性名称可以在jar包中找到) 内容: 建表sql: Mapper文件的头: 集成Mybatis的配置文件中的具体内容可以在 ...
- C++报错:error C3874
解决: 1.class 定义之后不要忘记分号 2.主函数写为int main() { }
- Nacos笔记01——使用Nacos作为SpringCloud项目的服务注册中心
前言 刚学SpringCloud时使用eureka作为服务注册中心,随着网飞公司eureka2.x不再更新,以及最近在公司实习接触到的SpringCloud项目是使用Nacos来做服务注册中心的,所以 ...
- 自己用ansible加shell 写的自动安装kubernetes的脚本
脚本地址:https://github.com/shatianxiaozi/auto_install_k8s.git 1. 下载 git clone https://github.com/shatia ...
- Go net/http 发送常见的 http 请求
使用 golang 中的 net/http 包来发送和接收 http 请求 开启 web server 先实现一个简单的 http server,用来接收请求 package main import ...
- Java 之 递归
一.概述 递归:指在当前方法内调用自己的现象. 递归的分类: 递归分为两种,直接递归和简介递归 直接递归称为方法自身调用自己 间接递归可以 A 方法调用 B 方法,B 方法调用 C 方法,C 方法调用 ...
- ubuntu18.04 安装android studio
首先从官网下载android studio:Android Studio (安装前应先安装JDK环境) 得到android-studio-ide-191.5977832-linux.tar.gz 在安 ...
- Android笔记(二十四) Android中的SeekBar(拖动条)
拖动条和进度条非常相似,只是进度条采用颜色填充来表明进度完成的程度,而拖动条则通过滑块的位置来标识数值——而且拖动条允许用户拖动滑块来改变值,因此拖动条通常用于对系统的某种数值进行调节,比如调节音量等 ...
- Windows——Office使用激活工具激活后仍提示激活
问题: Office使用激活工具激活后仍提示激活 分析: 造成该问题的原因通常是未删除操作系统预置Office导致的, 解决方案: 调出运行,输入regedit打开注册表编辑器, 依次打开 HKE ...
- RTC — 软件协作开发管理平台
IBM Rational Team Concert (简称RTC )是构建在IBM Rational面向软件交付技术的下一代协作平台Jazz平台上的一个商用产品.一个协作式的软件开发环境,它包含了集成 ...