带尾置返回类型的C++模板函数中decltype为何需用模板参数?
为何模板函数中decltype调用无参getBox报错,带模板参数的调用却正常?
问题现象
两个模板函数均未提前声明getBox函数,但编译结果完全不同:
无编译错误的案例:
// case 1: no error template <typename T> struct basic_type { using type = T; }; template<typename T> auto getBox1() -> decltype((getBox(basic_type<T>{}))) { return getBox(basic_type<T>{}); }
触发语法错误的案例:
// case 2: error template <typename T> struct basic_type { using type = T; }; template<typename T> auto getBox1() -> decltype((getBox())) // 错误提示:"identifier "getBox" is undefined" { return getBox(); }
核心原因:C++模板的两阶段名称查找规则
差异源于C++标准规定的模板两阶段名称查找机制,以及依赖名称与非依赖名称的区分:
非依赖名称的查找(案例2)
案例2中getBox()的调用没有任何与模板参数T相关的部分,因此getBox属于非依赖名称。根据规则,非依赖名称必须在模板**定义阶段(第一阶段)**就找到有效的声明。由于此时没有任何getBox的声明,编译器直接触发"标识符未定义"的错误,且这个错误在模板定义时就会出现,与是否实例化模板无关。依赖名称的查找(案例1)
案例1中getBox(basic_type<T>{})的调用包含了依赖模板参数T的表达式basic_type<T>{},因此整个函数调用表达式属于依赖于模板参数的名称。对于依赖名称,编译器会将查找推迟到模板实例化阶段(第二阶段):- 在模板定义时,编译器仅检查语法正确性,不会去查找
getBox的具体声明; - 只有当模板被实际实例化时,编译器才会在模板实例化点的作用域、模板定义时的作用域(以及通过ADL查找相关命名空间)中寻找
getBox的声明。如果实例化时仍找不到,才会报错;若模板从未被实例化,则不会触发错误。
- 在模板定义时,编译器仅检查语法正确性,不会去查找
补充验证
- 若案例1的模板
getBox1永远不被实例化,编译全程不会报错; - 若实例化案例1时,当前作用域仍无
getBox的声明,此时才会触发与案例2类似的未定义错误。
内容的提问来源于stack exchange,提问作者EthanLee
相关产品推荐
相关产品推荐

