含decltype(lambda)默认模板参数的成员函数为何在GCC编译失败?
GCC与Clang对带lambda默认模板参数的成员函数处理差异
代码案例对比
案例1:类模板成员函数模板
以下代码可在Clang trunk正常编译,但GCC trunk编译失败,仅提示模板参数推导失败,无额外诊断信息:
template<typename> struct foo { template<typename = decltype([]{})> void bar() {} }; int main() { [[maybe_unused]] auto v = foo<void>(); v.bar<int>(); // OK v.bar<decltype([]{})>(); // OK // 注释掉这两行则可编译: v.bar<>(); // 推导失败 v.bar(); // 推导失败 }
GCC编译错误输出:
<source>: In function 'int main()': <source>:15:12: error: no matching function for call to 'foo<void>::bar<>()' 15 | v.bar<>(); // deduction failure | ~~~~~~~^~ <source>:4:10: note: candidate: 'template<class> void foo< <template-parameter-1-1> >::bar() [with <template-parameter-1-1> = void]' 4 | void bar() {} | ^~~ <source>:4:10: note: template argument deduction/substitution failed: <source>:16:10: error: no matching function for call to 'foo<void>::bar()' 16 | v.bar(); // deduction failure | ~~~~~^~ <source>:4:10: note: candidate: 'template<class> void foo< <template-parameter-1-1> >::bar() [with <template-parameter-1-1> = void]' 4 | void bar() {} | ^~~ <source>:4:10: note: template argument deduction/substitution failed: Compiler returned: 1
案例2:全局函数模板
有趣的是,以下全局函数模板代码可在两款编译器上正常编译:
template<typename = decltype([]{})> void foo() {} int main() { foo<>(); foo(); }
核心问题
- 若这并非GCC的bug,为何第一个代码片段无法在GCC上编译?
- 为何GCC仅输出无详细内容的推导失败提示?
分析推测
首先排除lambda类型与链接符号的关联推测:虽然C++标准[temp.fct]注4提到lambda类型不能出现在任何“链接”符号中,但[defns.signature.template.head]明确将默认参数排除在模板头签名之外,因此这并非核心原因。
问题的关键可能在于类模板实例化时,成员函数模板默认参数的处理时机与全局函数模板不同:
- 对于全局函数模板,
decltype([]{})的lambda类型会在模板首次被实例化或需要推导默认参数时确定,全局上下文的lambda类型推导逻辑相对直接。 - 对于类模板内部的成员函数模板,GCC可能在类模板实例化(即
foo<void>实例化)时就尝试解析默认参数的lambda类型,但此时成员函数模板尚未被实例化,导致GCC无法正确推导这个默认模板参数的类型,进而在调用v.bar<>()或v.bar()时,无法匹配到正确的函数模板实例。
而Clang则可能延迟了成员函数模板默认参数的解析时机,直到成员函数模板被实际调用时才处理decltype([]{})的推导,因此可以正常匹配。
关于GCC的诊断信息简略问题,这可能是因为GCC在处理模板参数推导失败时,对于涉及lambda类型的默认参数推导错误,没有专门的诊断分支,只能输出通用的“推导失败”提示,无法提供更具体的原因说明。
内容的提问来源于stack exchange,提问作者Nitori Kawashiro
相关产品推荐
相关产品推荐

