C++类型特性中_t别名与_v变量模板的存在意义及选用疑问
_v/_t后缀工具类的常见疑问 1. 为什么会有_v/_t这类工具?
这俩就是C++标准委员会给开发者搞的语法糖,用来解决旧写法的繁琐问题。
在C++14之前,要获取类型特性的结果,你得写std::is_same<T, U>::value(取布尔值)或者typename std::result_of<F(Args...)>::type(取类型)——后者还必须加typename,编译器才知道::type是个类型,稍不注意就写错。
C++14引入_t(类型别名)和_v(值常量)后,写法直接简化:
std::is_same_v<T, U>等价于std::is_same<T, U>::valuestd::invoke_result_t<F, Args...>等价于typename std::invoke_result<F, Args...>::type
对比一下代码量和可读性,优劣一目了然。
2. 暴露::type/::value这类“实现细节”有益吗?
首先得纠正:这不是什么“实现细节”,而是C++模板元编程的基础机制。
早期的模板元编程全靠嵌套类型(::type)和静态值(::value)来传递信息,比如SFINAE技术的核心就是检测某个类型特性的::type是否存在。举个例子,C++14之前要写一个仅当类型是整数时生效的函数,就得这么写:
template<typename T> typename std::enable_if<std::is_integral<T>::value, void>::type print_int(T val) { /* ... */ }
这里必须用到std::is_integral<T>::value,没有它就玩不转SFINAE。
另外,当你需要把类型特性本身作为模板参数传递时,也得用旧版本——std::is_same<T, U>是个模板类,而std::is_same_v只是个布尔值,用途完全不同。比如你要自定义一个继承自现有类型特性的新特性,就必须基于模板类来写。
3. 忽略标准合规性,是否该始终优先用_v/_t?
从代码简洁性和可读性来说,优先选_v/_t绝对是更好的选择。少写冗余的typename和::type/::value,代码更清爽,也能避免很多低级编译错误。
但也不是绝对的:如果你的代码需要对类型特性做扩展(比如继承、组合现有特性),或者要利用模板类的其他嵌套成员,那还是得用旧版本的写法。
4. ::type/::value版本是不是根本没必要存在?
当然不是。_t/_v本身就是基于旧版本实现的——标准里明确规定std::invoke_result_t<F, Args...>就是typename std::invoke_result<F, Args...>::type的别名,std::is_same_v<T, U>就是std::is_same<T, U>::value的常量表达式。没有旧版本,新的语法糖就成了无源之水。
而且,很多遗留项目或者需要兼容C++14之前标准的代码,只能用旧写法。另外,模板元编程的高级玩法(比如自定义类型特性、手写概念、复杂的SFINAE逻辑)都离不开这些嵌套类型和静态值的机制,它们是元编程的基础构件,不是可有可无的。
内容的提问来源于stack exchange,提问作者KamilCuk

