为何std::conditional会拒绝T&?如何正确结合二者使用?
解决std::conditional结合void与T&的编译错误
想要实现模板参数为void时返回void,为其他类型T时返回T&,编写了如下代码:
#include<type_traits> template<class T> struct Foo { using X = std::conditional<std::is_void_v<T>, void, T&>::type; X bar(){ } }; int main(){ Foo<void> foo; return 0; }
使用g++-13 --std=c++20 main.cpp或clang++ --std=c++20 main.cpp编译时出现错误:
main.cpp:5:58: error: cannot form a reference to 'void' 5 | using X = std::conditional<std::is_void_v<T>, void, T&>::type; | ^ main.cpp:11:15: note: in instantiation of template class 'Foo<void>' requested here 11 | Foo<void> foo1; | ^ 1 error generated.
报错原因
std::conditional的两个模板参数分支都会被编译器实例化检查,即使其中一个分支不会被选中。当T为void时,第二个分支的T&会被解析为void&,而C++标准明确禁止创建void的引用类型,因此触发编译错误。
正确实现方式
方法一:利用标准库的std::add_lvalue_reference
C++标准库中std::add_lvalue_reference的行为恰好符合需求:对void类型返回void,对其他类型返回对应的左值引用。直接使用即可:
#include<type_traits> template<class T> struct Foo { using X = std::add_lvalue_reference_t<T>; X bar(){ } }; int main(){ Foo<void> foo; Foo<int> foo2; return 0; }
方法二:自定义模板特化
如果需要更明确的逻辑控制,可以通过模板特化实现辅助结构:
#include<type_traits> // 通用模板:返回T& template<typename T> struct ref_or_void { using type = T&; }; // void特化:返回void template<> struct ref_or_void<void> { using type = void; }; template<class T> struct Foo { using X = typename ref_or_void<T>::type; X bar(){ } }; int main(){ Foo<void> foo; Foo<int> foo2; return 0; }
方法三:延迟实例化分支类型
通过将T&包装在另一个模板中,延迟其实例化时机,避免void场景下的非法类型检查:
#include<type_traits> template<typename T> struct add_lref { using type = T&; }; template<class T> struct Foo { using X = typename std::conditional<std::is_void_v<T>, std::type_identity_t<void>, add_lref<T>>::type::type; X bar(){ } }; int main(){ Foo<void> foo; Foo<int> foo2; return 0; }
这里利用std::type_identity(C++20引入)保持void类型,而add_lref<T>只有在条件为真时才会被访问::type,从而避免void场景下实例化add_lref<void>。
内容的提问来源于stack exchange,提问作者chabapok
相关产品推荐
相关产品推荐

