关于模板函数尾置返回类型中type_identity的作用及自制实现的疑问
type_identity的作用及自制实现的疑问 嘿,这个问题问得特别好!咱先把核心差异拎出来:cppreference的实现和你的实现,本质上是获取目标类型的方式不一样,这就是为啥它需要type_identity而你的代码看起来能正常跑的原因~
先拆解cppreference的思路:它是让add_lvalue_reference直接继承自decltype(...)的结果。那问题来了——如果直接让try_add_lvalue_reference返回T&或者T,那decltype(...)得到的就是T&、T或者void这些类型。但C++里是不允许继承引用类型、基本类型或者void的!比如你要是写struct Foo : int& {};,编译器直接给你报错。
所以type_identity的核心作用就是把目标类型包装成一个结构体:当try_add_lvalue_reference返回type_identity<T&>时,decltype(...)得到的是一个结构体类型(type_identity<int&>这种),而继承结构体是完全合法的。同时,这个结构体自带type成员,add_lvalue_reference继承它之后,就自动拥有了type这个别名,不用自己再写一遍,相当于是一种代码复用的技巧。
再看你的实现:你没有用继承的方式,而是在add_lvalue_reference结构体内部直接定义type = decltype(...)。这时候decltype推导出来的T&、T或者void只是作为类型别名的目标,完全不存在“继承非法类型”的问题——毕竟只是定义一个别名,又不是继承它。所以你的代码在测试用例里能正常跑,是完全合理的。
那是不是你的实现就完全没问题?其实在符合标准要求的场景下,你的写法是完全正确的,和cppreference的实现效果一致——因为标准只要求add_lvalue_reference和add_rvalue_reference必须提供type成员类型,两种写法都满足这个要求。
举个反例就能更清楚了:如果cppreference的实现去掉type_identity,改成下面这样:
namespace detail { template <class T> auto try_add_lvalue_reference(int) -> T&; template <class T> auto try_add_lvalue_reference(...) -> T; } template <class T> struct add_lvalue_reference : decltype(detail::try_add_lvalue_reference<T>(0)) {};
当T是int时,decltype(...)是int&,这时候struct add_lvalue_reference : int& {};就是非法代码,编译器直接报错。但用了type_identity之后,继承的是结构体,就完全没问题了。
总结一下:两种实现各有各的思路,cppreference用继承复用type成员,所以必须靠type_identity包装类型;你的写法直接定义type成员,绕开了继承的限制,所以不需要type_identity。你的测试用例能通过,是因为这种写法确实符合标准要求~
备注:内容来源于stack exchange,提问作者cpp

