C++20中std::reference_wrapper<void>为何不会触发编译错误?
问题原因分析
核心原因是类模板约束的作用位置不同,以及C++对「指向不完整类型的指针」的语法许可,具体分两点说明:
1. 自定义模板A报错的原因
你给A加的requires约束是类模板顶层关联约束,C++20标准规定:只要你在代码中命名了A<T>这个特化类型,不管你是否需要实例化该类、是否只用到它的指针,都必须先检查类模板的顶层约束是否满足。
A<void>直接触发了!std::is_void_v<T>的约束检查,约束不满足直接编译错误,和你是否声明指针无关。
2. std::reference_wrapper<void>*不报错的原因
标准库的std::reference_wrapper没有设置类模板顶层约束,所有和类型合法性相关的约束都只作用在它的成员函数上(比如构造函数、get()方法、operator()重载)。
这意味着std::reference_wrapper<void>这个类型名本身是合法的,属于未实例化的不完整类型。而C++语法一直允许声明指向不完整类型的指针,不需要该类型完全实例化、也不需要检查成员函数的约束,所以std::reference_wrapper<void>* p2{}的声明不会触发任何约束检查,自然不会报错。
验证示例
如果你尝试创建std::reference_wrapper<void>的实例,就会触发成员函数的约束检查,得到预期的编译错误:
std::reference_wrapper<void> r; // 编译报错,符合预期
同理,如果你把自定义模板A的约束从类顶层移到构造函数上,A<void>*的声明也不会报错:
template<typename T> struct A { A() requires (!std::is_void_v<T>) = default; }; A<void>* p1{}; // 不再报错,和reference_wrapper行为一致
内容的提问来源于stack exchange,提问作者xmllmx
相关产品推荐
相关产品推荐

