C++标准为何允许通过依赖typedef-name继承构造函数?
继承构造函数自C11起便已存在,但令人困惑的是,C标准为何允许将typedef-name作为依赖名用于using声明中。
以下代码可正常运行(这确实简化了不少操作,但原理让人费解):
#include <iostream> template <class T> class foo { T m_data = 0; public: explicit foo () = default; explicit foo (T &&idata): m_data (std::move(idata)) {} foo (foo<T> const &) = default; auto data() const noexcept { return m_data; } }; template <class T> class bar: public foo<T> { typedef foo<T> base_type; public: using base_type::base_type; // 此写法可行,但原理是什么? // error: out-of-line constructor for 'foo' cannot have template arguments //using foo<T>::foo<T>; }; int main() { bar <int> b {3}; return b.data(); }
这段代码已在多款编译器(含MSVC)中测试,均能正常运行。
核心疑问是:当base_type是typedef名时,标准为何能将base_type::base_type识别为基类构造函数。base_type与foo<T>等价,但using foo<T>::foo<T>是语法错误。(注:并非询问后者为何不可行,而是想知道前者可行的原因)
原理解析
继承构造函数的语法规则
C++规定,继承构造函数的合法语法是using 基类名::构造函数名;,而类的构造函数名本身就和类名完全一致。当使用typedef别名base_type指代基类foo<T>时,base_type::base_type里的第二个base_type会被编译器自动解析为基类的构造函数名(即基类的类名),完全符合继承构造函数的语法要求。typedef别名的等价性处理
在C++标准中,typedef别名是其指代类型的等价名称。编译器在处理using base_type::base_type;时,会将base_type替换为它所指代的foo<T>,此时语句就等价于using foo<T>::foo;——这是完全合法的继承构造函数声明,自然可以正常编译。为何
foo<T>::foo<T>不合法
后者的错误在于试图给构造函数添加模板参数列表foo<T>,但构造函数本身并不是模板(即使是类模板的构造函数,也不能在using声明中这样显式指定模板参数),这违反了C++对继承构造函数声明的语法约束,因此会触发编译错误。
内容的提问来源于stack exchange,提问作者Spencer

