显然 class 模板成员的错误初始化编译得很好
Apparently erroneous initialization of class template member compiles just fine
考虑以下 MCVE 代码:
struct Id { static inline int id(int c) {return c;} };
template <class C>
class Foo {
C m_bar;
public:
Foo() = default;
Foo(int) : m_bar{bar} {} // Fails in clang, compiles in gcc/msvc
Foo(int, int) : m_bar(bar) {} // Compiles with all gcc/clang/msvc
Foo(int, int, int) : m_bar(Id::id(bar)) {} // Compiles in gcc/msvc with both () and {} syntax, fails for both in clang
C bar() const { return m_bar; }
};
为简单起见,假设我实例化了 Foo<int> x;
。我希望所有构造函数都会产生编译错误,因为我试图用 bar
初始化 m_bar
,这是一个 方法 (不是我此处不调用 bar
)。
但是所有变体都可以在所有主要编译器上编译,唯一的例外是Foo(int)
。
请注意,当我调用相应的构造函数重载时,所有编译器都会按预期失败,例如Foo<int> f{1};
。但据我了解,class 是一个模板,而不是 constructor,所以我希望所有构造函数在以下情况下都会失败实例化 class 模板,甚至是未使用的模板。
有趣的是,当使用如下所示的非模板 class 时,所有三个构造函数都会按预期失败:
class Foo {
int m_bar;
public:
Foo() = default;
Foo(int) : m_bar{bar} {} // fails in all gcc/clang/msvc
Foo(int, int) : m_bar(bar) {} // fails in all gcc/clang/msvc
Foo(int, int, int) : m_bar{Id::id(bar)} {} // fails in all gcc/clang/msvc
int bar() const { return m_bar; }
};
为方便起见,这里是 godbolt link 和编译器输出。
我的问题是:这是所有三个编译器中的错误,还是我这边存在一些误解,也许是标准中解释此行为的一些模糊部分?
这是因为 class 模板和构造函数模板之间的唯一区别是 class 模板在整个模板中是全局的 class 但是构造函数模板不是全局模板class 并且它们在作用域的末尾被删除。每个成员函数也是单独实例化的,因此永远不会调用有错误的构造函数。人们可能使用构造函数模板而不是 class 模板的主要原因是因为他们可能需要一些非模板构造函数。
您还可以查看 this 以了解有关 class 模板和构造函数模板的更多信息。
Class 模板成员按需实例化。见 [temp.inst]/9:
An implementation shall not implicitly instantiate a function template, a variable template, a member template, a non-virtual member function, a member class, a static data member of a class template, or a substatement of a constexpr if statement, unless such instantiation is required.
此外,由于 m_bar
依赖于模板参数,表达式 m_bar{bar}
无法在模板解析期间进行语法检查。
考虑以下 MCVE 代码:
struct Id { static inline int id(int c) {return c;} };
template <class C>
class Foo {
C m_bar;
public:
Foo() = default;
Foo(int) : m_bar{bar} {} // Fails in clang, compiles in gcc/msvc
Foo(int, int) : m_bar(bar) {} // Compiles with all gcc/clang/msvc
Foo(int, int, int) : m_bar(Id::id(bar)) {} // Compiles in gcc/msvc with both () and {} syntax, fails for both in clang
C bar() const { return m_bar; }
};
为简单起见,假设我实例化了 Foo<int> x;
。我希望所有构造函数都会产生编译错误,因为我试图用 bar
初始化 m_bar
,这是一个 方法 (不是我此处不调用 bar
)。
但是所有变体都可以在所有主要编译器上编译,唯一的例外是Foo(int)
。
请注意,当我调用相应的构造函数重载时,所有编译器都会按预期失败,例如Foo<int> f{1};
。但据我了解,class 是一个模板,而不是 constructor,所以我希望所有构造函数在以下情况下都会失败实例化 class 模板,甚至是未使用的模板。
有趣的是,当使用如下所示的非模板 class 时,所有三个构造函数都会按预期失败:
class Foo {
int m_bar;
public:
Foo() = default;
Foo(int) : m_bar{bar} {} // fails in all gcc/clang/msvc
Foo(int, int) : m_bar(bar) {} // fails in all gcc/clang/msvc
Foo(int, int, int) : m_bar{Id::id(bar)} {} // fails in all gcc/clang/msvc
int bar() const { return m_bar; }
};
为方便起见,这里是 godbolt link 和编译器输出。
我的问题是:这是所有三个编译器中的错误,还是我这边存在一些误解,也许是标准中解释此行为的一些模糊部分?
这是因为 class 模板和构造函数模板之间的唯一区别是 class 模板在整个模板中是全局的 class 但是构造函数模板不是全局模板class 并且它们在作用域的末尾被删除。每个成员函数也是单独实例化的,因此永远不会调用有错误的构造函数。人们可能使用构造函数模板而不是 class 模板的主要原因是因为他们可能需要一些非模板构造函数。
您还可以查看 this 以了解有关 class 模板和构造函数模板的更多信息。
Class 模板成员按需实例化。见 [temp.inst]/9:
An implementation shall not implicitly instantiate a function template, a variable template, a member template, a non-virtual member function, a member class, a static data member of a class template, or a substatement of a constexpr if statement, unless such instantiation is required.
此外,由于 m_bar
依赖于模板参数,表达式 m_bar{bar}
无法在模板解析期间进行语法检查。