Ami*_*rsh 6 c++ static scope definition language-lawyer
C++ 规范(例如C++17 [class.static] §2)说:
静态数据成员的定义在其类的范围内。
举个例子(取自C++14 规范):
int g();
struct X {
static int g();
};
struct Y : X {
static int i;
};
int Y::i = g(); // equivalent to Y::g();
Run Code Online (Sandbox Code Playgroud)
规范中的确切措辞和示例多年来发生了变化,由于某些未知原因,C++11和C++14具有与上述非常相似的示例,C++17和C++20更喜欢初始化示例来自静态数据成员。在C ++ 20措辞也更准确。但似乎这条规则并没有实际的变化。
似乎这条规则不适用于模板化类的继承:
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;
}
Run Code Online (Sandbox Code Playgroud)
输出:
10 42 -1 999
Run Code Online (Sandbox Code Playgroud)
GCC 10.2 和 Clang 5.0.0 都同意上述(令人惊讶的?)输出。
它是正确的输出,还是两个编译器中的错误?
预期的输出,期望foo()
在静态成员初始化的上下文中调用 将表现为调用foobar::foo()
等,应该是:
10 10 -1 -1
Run Code Online (Sandbox Code Playgroud)
注意:继承的行为以及没有继承的模板类和带有非模板派生的模板基类的行为,似乎都可以正常工作。
GCC 和 Clang 都是正确的。
\n特别是,来自[temp.dep]/3 [重点是我的]:
\n\n\n在类或类模板的定义中,在非限定名称查找期间,无论是在类模板或成员的定义点,还是在类模板或成员的实例化期间,都不会检查依赖基类的范围。[\xe2\x80\x89示例:
\nRun Code Online (Sandbox Code Playgroud)\ntypedef double A;\ntemplate<class T> class B {\n typedef int A;\n};\ntemplate<class T> struct X : B<T> {\n A a; // a has type double\n};\n
\n
A
定义中的类型名称X<T>
绑定到typedef
全局命名空间范围中定义的名称,而不是typedef
基类中定义的名称class B<T>
。\xe2\x80\x89\xe2\x80\x94\xe2\x80\x89结束示例\xe2\x80\x89] [...]
在派生类模板的静态数据成员x1
和定义中:y1
foobar
template<int VALUE>\nint foobar<VALUE>::x1 = foobar::foo();\n\ntemplate<int VALUE>\nint foobar<VALUE>::y1 = foobar::z;\n
Run Code Online (Sandbox Code Playgroud)\n您正在使用注入类名称([temp.local]/3)来访问依赖名称([temp.res]/9)foo
并z
从base
类模板中访问,而静态数据定义中使用的非限定名称成员x2
,并且y2
不会检查依赖基类的范围。
请注意,我们可以将依赖类的名称引入base
派生类的范围,重新定义foobar
为
template<int VALUE>\nstruct foobar: base<VALUE> {\n using base<VALUE>::foo;\n using base<VALUE>::z;\n static int x1;\n static int x2;\n static int y1;\n static int y2;\n};\n
Run Code Online (Sandbox Code Playgroud)\n在这种情况下,你的程序的输出将是
\n10 10 -1 -1\n
Run Code Online (Sandbox Code Playgroud)\n