C++20概念中隐式转换的编译器行为合规性问询
std::is_*<T>{}隐式转换的编译器分歧 问题代码示例
#include <type_traits> template <typename T> concept is_non_const_lvalue_reference = std::is_lvalue_reference<T>{} && // GCC 12.2和MSVC 19.33认为格式错误 !std::is_const<std::remove_reference_t<T>>{}; static_assert(is_non_const_lvalue_reference<int&>); static_assert(!is_non_const_lvalue_reference<int const&>); static_assert(!is_non_const_lvalue_reference<int&&>);
现象描述
GCC 12.2与MSVC 19.33均判定std::is_lvalue_reference<T>{}的返回类型并非布尔值,因此概念定义格式错误;但Clang 15会执行到bool的隐式转换,认为该概念定义合法。而!std::is_const<...>{}表达式因!运算符的存在,会被编译器视为布尔表达式,三家编译器均无异议。
技术疑问
Clang 15执行到bool的隐式转换是否过于宽松?还是GCC和MSVC存在实现问题?抑或是C++20标准对此场景的隐式转换规则未作明确规定?
注:已知可通过std::is_*_v<T>替代std::is_*<T>{}规避该问题,但希望明确此类隐式转换的允许场景。
标准与编译器行为解析
C++20标准明确规定,概念的约束表达式必须是可上下文转换为bool的常量表达式,但不同场景下的转换规则有严格区别:
std::integral_constant的operator bool()属性:C++17起,std::integral_constant(包括std::is_lvalue_reference等类型特性)的operator bool()被定义为显式转换函数,这意味着它不能被隐式触发,必须通过显式转换(比如static_cast<bool>(...))才能得到布尔值。逻辑与
&&左侧操作数的转换规则:- GCC和MSVC的行为符合标准:在逻辑与运算中,左侧操作数不属于标准规定的“语境转换为bool”场景,因此无法触发显式的
operator bool()转换,编译器判定std::is_lvalue_reference<T>{}的类型不是布尔值,报错合理。 - Clang的行为属于非标准扩展:它在约束表达式的逻辑运算中放宽了规则,允许触发显式的
bool转换,这并非C++20标准要求的行为。
- GCC和MSVC的行为符合标准:在逻辑与运算中,左侧操作数不属于标准规定的“语境转换为bool”场景,因此无法触发显式的
!运算符的转换规则:!运算符的操作数属于标准规定的“语境转换为bool”场景,允许调用显式的operator bool()转换函数,因此!std::is_const<...>{}能被所有编译器正确处理,这完全符合标准。
结论:GCC和MSVC的行为符合C20标准,Clang的宽松转换属于非标准扩展,C20对此场景的规则是明确的。
内容的提问来源于stack exchange,提问作者Kyle Knoepfel

