显然 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} 无法在模板解析期间进行语法检查。