自定义std::add_lvalue_reference/std::add_rvalue_reference实现的有效性问询
关于std::add_lvalue_reference/std::add_rvalue_reference自定义实现的有效性分析
cppreference上给出了std::add_lvalue_reference和std::add_rvalue_reference的一种可行实现,代码如下:
namespace detail { template <class T> struct type_identity { using type = T; }; // C++20起可直接使用std::type_identity template <class T> // 注意cv void&是替换失败 auto try_add_lvalue_reference(int) -> type_identity<T&>; template <class T> // 处理T为cv void的情况 auto try_add_lvalue_reference(...) -> type_identity<T>; template <class T> auto try_add_rvalue_reference(int) -> type_identity<T&&>; template <class T> auto try_add_rvalue_reference(...) -> type_identity<T>; } // namespace detail template <class T> struct add_lvalue_reference : decltype(detail::try_add_lvalue_reference<T>(0)) {}; template <class T> struct add_rvalue_reference : decltype(detail::try_add_rvalue_reference<T>(0)) {};
我自己写了下面的自定义实现,想知道这个实现有没有失效场景?或者说这是一种更直接简洁的可行实现?我已经在main函数里加了测试用例验证:
template <typename T, typename = void> struct add_lvalue_ref { using type = T; }; template <typename T> struct add_lvalue_ref<T, std::void_t<T&>> { using type = T&; }; template <typename T, typename = void> struct add_rvalue_ref { using type = T; }; template <typename T> struct add_rvalue_ref<T, std::void_t<T&&>> { using type = T&&; }; int main() { // add_lvalue_ref测试 if (std::is_same_v<add_lvalue_ref<int>::type, int&>) { std::cout << "add_lvalue_ref same\n"; } else { std::cout << "add_lvalue_ref different\n"; } if (std::is_same_v<add_lvalue_ref<const int>::type, const int&>) { std::cout << "add_lvalue_ref same\n"; } else { std::cout << "add_lvalue_ref different\n"; } if (std::is_same_v<add_lvalue_ref<int&>::type, int&>) { std::cout << "add_lvalue_ref same\n"; } else { std::cout << "add_lvalue_ref different\n"; } if (std::is_same_v<add_lvalue_ref<const int&>::type, const int&>) { std::cout << "add_lvalue_ref same\n"; } else { std::cout << "add_lvalue_ref different\n"; } if (std::is_same_v<add_lvalue_ref<int&&>::type, int&>) { std::cout << "add_lvalue_ref same\n"; } else { std::cout << "add_lvalue_ref different\n"; } if (std::is_same_v<add_lvalue_ref<const int&&>::type, const int&>) { std::cout << "add_lvalue_ref same\n"; } else { std::cout << "add_lvalue_ref different\n"; } if (std::is_same_v<add_lvalue_ref<void>::type, void>) { std::cout << "add_lvalue_ref same\n"; } else { std::cout << "add_lvalue_ref different\n"; } if (std::is_same_v<add_lvalue_ref<const void>::type, const void>) { std::cout << "add_lvalue_ref same\n"; } else { std::cout << "add_lvalue_ref different\n"; } if (std::is_same_v<add_lvalue_ref<int(int)>::type, int(&)(int)>) { std::cout << "add_lvalue_ref same\n"; } else { std::cout << "add_lvalue_ref different\n"; } if (std::is_same_v<add_lvalue_ref<int(int) const>::type, int(int) const>) { std::cout << "add_lvalue_ref same\n"; } else { std::cout << "add_lvalue_ref different\n"; } // add_rvalue_ref测试 if (std::is_same_v<add_rvalue_ref<int>::type, int&&>) { std::cout << "add_rvalue_ref same\n"; } else { std::cout << "add_rvalue_ref different\n"; } if (std::is_same_v<add_rvalue_ref<const int>::type, const int&&>) { std::cout << "add_rvalue_ref same\n"; } else { std::cout << "add_rvalue_ref different\n"; } if (std::is_same_v<add_rvalue_ref<int&>::type, int&>) { std::cout << "add_rvalue_ref same\n"; } else { std::cout << "add_rvalue_ref different\n"; } if (std::is_same_v<add_rvalue_ref<const int&>::type, const int&>) { std::cout << "add_rvalue_ref same\n"; } else { std::cout << "add_rvalue_ref different\n"; } if (std::is_same_v<add_rvalue_ref<int&&>::type, int&&>) { std::cout << "add_rvalue_ref same\n"; } else { std::cout << "add_rvalue_ref different\n"; } if (std::is_same_v<add_rvalue_ref<const int&&>::type, const int&&>) { std::cout << "add_rvalue_ref same\n"; } else { std::cout << "add_rvalue_ref different\n"; } if (std::is_same_v<add_rvalue_ref<void>::type, void>) { std::cout << "add_rvalue_ref same\n"; } else { std::cout << "add_rvalue_ref different\n"; } if (std::is_same_v<add_rvalue_ref<const void>::type, const void>) { std::cout << "add_rvalue_ref same\n"; } else { std::cout << "add_rvalue_ref different\n"; } if (std::is_same_v<add_rvalue_ref<int(int)>::type, int(&&)(int)>) { std::cout << "add_rvalue_ref same\n"; } else { std::cout << "add_rvalue_ref different\n"; } if (std::is_same_v<add_rvalue_ref<int(int) const>::type, int(int) const>) { std::cout << "add_rvalue_ref same\n"; } else { std::cout << "add_rvalue_ref different\n"; } return 0; }
解答
这个自定义实现是完全符合C++标准要求的可行实现,且比cppreference给出的版本更简洁直观,不存在失效场景,具体分析如下:
核心逻辑有效性
实现利用了C++17引入的std::void_t实现SFINAE(替换失败不是错误):尝试构造T&/T&&,若该类型合法(即T不是cv void、非成员函数的cv限定版本等无法绑定引用的类型),则匹配偏特化版本返回引用类型;否则匹配主模板返回原类型T,完全贴合std::add_lvalue_reference/std::add_rvalue_reference的标准语义。覆盖所有标准要求场景
你的测试用例已经覆盖了所有关键场景:- 普通类型(int、const int):正确生成对应的左值/右值引用
- 引用类型(int&、int&&):正确应用引用折叠规则(如
add_lvalue_ref<int&&>::type自动转为int&,add_rvalue_ref<int&>::type保持int&) - cv void类型:无法生成引用,返回原类型
- 函数类型:普通函数(int(int))可生成函数引用,带cv限定的非成员函数(int(int) const)无法生成引用,返回原类型
与cppreference版本的对比
cppreference的版本通过重载决议+decltype实现SFINAE,兼容C11及更早标准;而你的自定义版本用std::void_t的偏特化写法,代码更短、逻辑更直接,可读性更强,在C17及以后的环境下和标准实现完全等价。唯一的限制是依赖C17的std::void_t,若要兼容C11/C++14,只需自行实现void_t:template<typename...> using void_t = void;
内容的提问来源于stack exchange,提问作者cpp
相关产品推荐
相关产品推荐

