带默认模板参数的Concept行为:编译器分歧属错误还是未指定?
GCC与C++ Concept实例化行为的争议问题
首先看这个带默认模板参数的concept定义:
template<class T, class = decltype([]{})> concept IsDefined = sizeof(T) > 0;
由于每个lambda的闭包类型都是唯一的,理论上每次IsDefined<X>的模板实例化都应该是独立的——默认参数里的lambda会在每次实例化时生成全新的类型,因此两次针对同一T的IsDefined实例化属于不同的模板特化。
接下来是测试代码:
struct SomeType; static_assert( false == IsDefined<SomeType> ); struct SomeType { // 已定义 }; static_assert( true == IsDefined<SomeType> );
这段代码的逻辑是:
- 前向声明
SomeType时,它属于不完整类型,sizeof(SomeType)无法合法计算,因此IsDefined<SomeType>应为false; - 完整定义
SomeType后,它成为完整类型,sizeof(SomeType)合法,IsDefined<SomeType>应为true。
实际编译表现:
- Clang与MSVC均可正常通过编译,行为符合预期;
- GCC会在处理第一个
static_assert时,触发第二个static_assert的失败错误,错误信息如下:
error: static assertion failed 17 | static_assert( true == IsDefined<SomeType> );
结论:这是GCC的实现错误
根据C++标准,当模板的默认参数是依赖于模板实例化的表达式(比如本例中的lambda,每次实例化都会生成新的闭包类型),每次模板实例化都应生成独立的默认参数类型。也就是说,两次IsDefined<SomeType>的实例化属于不同的特化,它们的求值结果应当分别对应SomeType在代码中两个位置的类型状态(不完整/完整)。
GCC错误地将两次IsDefined<SomeType>的实例化判定为同一个特化,导致在处理第一个static_assert时,就提前将第二个static_assert中的IsDefined<SomeType>绑定到不完整的SomeType,进而触发错误。这不符合C++标准的要求,属于编译器实现缺陷。
内容的提问来源于stack exchange,提问作者Drew Dormann
相关产品推荐
相关产品推荐

