"definition of a static data member is in the scope of its class" 的规范规则,模板化基础 class

Specification rule of "definition of a static data member is in the scope of its class" with a templated base class

C++ 规范(例如 C++17 [class.static] §2)说:

The definition of a static data member is in the scope of its class.

举个例子(摘自C++14 spec):

int g();
struct X {
  static int g();
};
struct Y : X {
  static int i;
};
int Y::i = g();                 // equivalent to Y::g();

规范中的确切措辞和示例多年来发生了变化,由于某些未知原因,C++11 and C++14 have a quite similar example as the above, C++17 and C++20 preferred an example initializing from a static data member. The C++20 措辞也更加简洁。但是这个规则好像并没有实际的变化。


似乎这条规则 doesn't work well 用于继承模板 class:

template<int VALUE>
struct base {
    static int foo() { return VALUE; }
    static constexpr int z = -1;
};

template<int VALUE>
struct foobar: base<VALUE> {
    static int x1;
    static int x2;
    static int y1;
    static int y2;
};

int foo() { return 42; }

int z = 999;

template<int VALUE>
int foobar<VALUE>::x1 = foobar::foo();

template<int VALUE>
int foobar<VALUE>::x2 = foo();

template<int VALUE>
int foobar<VALUE>::y1 = foobar::z;

template<int VALUE>
int foobar<VALUE>::y2 = z;

int main() {
    std::cout << foobar<10>::x1 << ' ' << foobar<10>::x2 << ' '
              << foobar<10>::y1 << ' ' << foobar<10>::y2;
}

输出:

10 42 -1 999

GCC 10.2 和 Clang 5.0.0 都同意上述(令人惊讶?)输出。

这是正确的输出,还是两个编译器中的错误?

在静态成员初始化的上下文中调用 foo() 的预期输出将表现为调用 foobar::foo() 等,应该是:

10 10 -1 -1

注意:inheritance as well as for a template class without inheritance and a template base with a non-template derived 的行为似乎一切正常。

GCC 和 Clang 都是正确的。

特别是来自 [temp.dep]/3 [强调 我的]:

In the definition of a class or class template, the scope of a dependent base class is not examined during unqualified name lookup either at the point of definition of the class template or member or during an instantiation of the class template or member. [ Example:

typedef double A;
template<class T> class B {
  typedef int A;
};
template<class T> struct X : B<T> {
  A a;              // a has type double
};

The type name A in the definition of X<T> binds to the typedef name defined in the global namespace scope, not to the typedef name defined in the base class B<T>.  — end example ] [...]

在派生 class 模板 foobar:

的静态数据成员 x1y1 的定义中
template<int VALUE>
int foobar<VALUE>::x1 = foobar::foo();

template<int VALUE>
int foobar<VALUE>::y1 = foobar::z;

您正在使用 注入的-class-名称 ([temp.local]/3) to access the dependent names ([temp.res]/9) fooz 来自24=] class 模板,而静态数据成员 x2y2 定义中使用的非限定名称将不会检查依赖基 class 的范围。


请注意,我们可能会将依赖 base class 的名称引入派生 class 的范围内,重新定义 foobar

template<int VALUE>
struct foobar: base<VALUE> {
    using base<VALUE>::foo;
    using base<VALUE>::z;
    static int x1;
    static int x2;
    static int y1;
    static int y2;
};

在这种情况下,您的程序的输出将是

10 10 -1 -1

这只是非依赖名称绑定规则的另一种情况,在模板定义点查找和绑定。 (不要与 ADL 混淆!即使 ADL 可以帮助调用 foo() 如果它有任何参数。)
有关完整详细信息,请阅读此处:https://en.cppreference.com/w/cpp/language/dependent_name

从模板库 class:

调用函数时会发生类似的工件
template <typename T>
struct A
{
    void f(){}
};

template <typename T>
struct B : A<T>
{
    void g()
    {
        A<T>::f();     // of course works
        this->f();     // this-> makes it depended, so it's bound only on instantiation time

        f();           // compilation error, no f() found

        using A<T>::f; // make the name available (and depended on the template)
        f();           // now it works
    }
};

https://gcc.godbolt.org/z/39KGvM