默认模板实参类型是否需每次实例化推导?编译器行为差异问询
类模板成员函数模板默认实参的lambda类型推导问题
这段代码在GCC和MSVC中可正常编译,但Clang会报错。由于任意两个lambda的类型均不相同,推测Clang会“缓存”类模板中函数模板的默认模板实参类型。
补充说明:我期望每次实例化函数模板时,都能推导默认实参的类型,但在第三个测试用例(Test2)中,Clang仅在首次使用时实例化该函数模板。这一行为不仅与另外两款主流编译器不一致,甚至与Clang自身在其他场景(自由函数、非类模板中的成员函数)的行为相悖——在另外两个测试用例中,每次实例化都会推导类型。
请问该场景是否有明确的标准规则?还是属于未定义/未指定/实现相关的行为?
#include <type_traits> template <typename T = decltype([](){})> T f(); struct Test1 { template <typename T = decltype([](){})> T f(); }; template <typename = void> struct Test2 { template <typename T = decltype([](){})> T f(); }; int main() { static_assert(!std::is_same_v<decltype(f()), decltype(f())>); Test1 test1; static_assert(!std::is_same_v<decltype(test1.f()), decltype(test1.f())>); Test2 test2; static_assert(!std::is_same_v<decltype(test2.f()), decltype(test2.f())>); // GCC和MSVC编译通过,Clang报错 }
问题解析与标准依据
这个场景的核心是模板默认实参的实例化时机与lambda类型的唯一性,C++标准对此有明确规则:
- lambda类型的唯一性:根据C++标准,每个lambda表达式都会生成一个唯一的未命名非联合类类型,即使两个lambda的代码完全一致,它们的类型也不相同。因此每次计算
decltype([](){})都应得到全新的类型。 - 类模板成员函数模板的默认实参处理:类模板(如Test2)中的成员函数模板,其默认模板实参属于函数模板本身,而非类模板的实例。标准要求,每次实例化函数模板时,都需要重新求值其默认实参。
Clang的行为不符合标准:它错误地将类模板实例化时的函数模板默认实参“固化”,导致后续调用test2.f()时复用了首次推导的lambda类型,从而触发static_assert失败。而GCC和MSVC的行为符合标准,每次调用都会重新推导默认实参的类型,生成新的lambda类型。
该场景不属于未定义/未指定行为,标准有明确要求,Clang的表现属于编译器实现错误。
内容的提问来源于stack exchange,提问作者Dmitry
相关产品推荐
相关产品推荐

