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和 C++14有一个与上面非常相似的例子,C++17和 C++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 inheritance和 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 ofX<T>
binds to thetypedef
name defined in the global namespace scope, not to thetypedef
name defined in the baseclass B<T>
. — end example ] [...]
在定义中,静态数据成员
x1
和 y1
派生类模板的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 ) foo
和 z
来自 base
类模板,而静态数据成员定义中使用的非限定名称x2
和 y2
不会检查依赖基类的范围。请注意,我们可能会引入依赖
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/