Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
293 views
in Technique[技术] by (71.8m points)

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

The C++ specification (e.g. C++17 [class.static] §2) says:

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

With an example (taken from C++14 spec):

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

The exact wording and example in the specification have changed over the years, for some unknown reasons, 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 wording is also much more concise. But it seems that there is no actual change in this rule.


It seems that this rule doesn't work well for inheritance of a templated 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;
}

Output:

10 42 -1 999

Both GCC 10.2 and Clang 5.0.0 agree on the above (surprising?) output.

Is it the right output, or is it a bug in both compilers?

The expected output, expecting that calling foo(), in the context of a static member initialization, would behave as calling foobar::foo(), etc., should be:

10 10 -1 -1

Note: the behavior for inheritance as well as for a template class without inheritance and a template base with a non-template derived, all seems to work correctly.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

GCC and Clang are both correct.

Particularly, from [temp.dep]/3 [emphasis mine]:

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?] [...]

In the definitions the static data members x1 and y1 of the of derived class template foobar:

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

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

you are using the injected-class-name ([temp.local]/3) to access the dependent names ([temp.res]/9) foo and z from the base class template, whereas the unqualified names used in the definitions of the static data members x2 and y2 will not examine the scope of the dependent base class.


Note that we may bring in the names of the dependent base class into the scope of the derived class, redefining foobar as

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;
};

in which case the output of your program will be

10 10 -1 -1

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...