c++ - 带有模板基类的 "definition of a static data member is in the scope of its class"规范规则

标签 c++ scope static language-lawyer definition

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++11C++14有一个与上面非常相似的例子,C++17C++20首选从静态数据成员初始化的示例。 C++20措辞也更加简洁。但似乎这条规则并没有实际的变化。

看来这条规则doesn't work well对于模板类的继承:
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 的行为以及 a template class without inheritancea 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 ] [...]


在定义中,静态数据成员 x1y1派生类模板的foobar :
template<int VALUE>
int foobar<VALUE>::x1 = foobar::foo();

template<int VALUE>
int foobar<VALUE>::y1 = foobar::z;
您正在使用注入(inject)的类名 ( [temp.local]/3 ) 来访问从属名称 ( [temp.res]/9 ) fooz来自 base类模板,而静态数据成员定义中使用的非限定名称x2y2不会检查依赖基类的范围。

请注意,我们可能会引入依赖 base 的名称。类进入派生类的范围,重新定义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

关于c++ - 带有模板基类的 "definition of a static data member is in the scope of its class"规范规则,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65597681/

相关文章:

c++ - 非 constexpr 变量有时可用于 constexpr 上下文?

c++ - Xcode 初学者需要帮助开始调试

qt - 在其他其他QML文件的QML中声明全局属性

宿主函数返回后事件中可用的 JavaScript 局部范围变量。为什么?

javascript - 如何在 Node 中共享作用域和变量?

C++ 对每个新对象使用相同的 default_random_engine

static - Nuxt js 全静态动态页面,payload undefined

c++ - 通过引用传递 vector 并在基于范围的 for 循环中更改其值?

c++ - 如何使用 log4cplus 在日志文件中打印方法名称

Java - 非静态类的扩展静态类