如何解决带有模板化基类的“静态数据成员的定义在其类的范围内”的规范规则
C++ 规范(例如 C++17 [class.static] §2)说:
静态数据成员的定义在其类的范围内。
举个例子(取自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 和 {{ 3}} 首选从静态数据成员初始化的示例。 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;
}
输出:
10 42 -1 999
GCC 10.2 和 Clang 5.0.0 都同意上述(令人惊讶?)输出。
预期的输出,期望在静态成员初始化的上下文中调用 foo()
,将表现为调用 foobar::foo()
等,应该是:
10 10 -1 -1
注意:doesn't work well 以及 inheritance 和 a template class without inheritance 的行为似乎都可以正常工作。
解决方法
GCC 和 Clang 都是正确的。
特别是来自[temp.dep]/3 [强调我的]:
在类或类模板的定义中,一个作用域 在非限定名称查找期间不检查依赖基类 在类的定义点模板或成员或 在类模板或成员的实例化期间。 [ 示例:
typedef double A;
template<class T> class B {
typedef int A;
};
template<class T> struct X : B<T> {
A a; // a has type double
};
A
定义中的类型名称 X<T>
绑定到 typedef
在全局命名空间范围内定义的名称,而不是 typedef
名称
在基 class B<T>
中定义。 — 结束示例 ] [...]
在定义中派生类模板x1
的静态数据成员y1
和foobar
:
template<int VALUE>
int foobar<VALUE>::x1 = foobar::foo();
template<int VALUE>
int foobar<VALUE>::y1 = foobar::z;
您正在使用 injected-class-name ([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
,
这只是非依赖名称绑定规则的另一种情况,在模板定义点查找和绑定。 (不要与 ADL 混淆!即使 ADL 会帮助调用 foo()
,如果它有任何参数。)
有关完整的详细信息,请阅读此处:https://en.cppreference.com/w/cpp/language/dependent_name
从模板基类调用函数会发生类似的工件:
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
}
};
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。