通过使用派生类的静态constexpr数据成员来初始化基类的静态constexpr数据成员 [英] Initializing a static constexpr data member of the base class by using a static constexpr data member of the derived class
问题描述
请考虑以下代码:
template<typename T>
struct S { static constexpr int bar = T::foo; };
struct U: S<U> { static constexpr int foo = 42; };
int main() { }
GCC v6.1 编译它, clang 3.8 拒绝它的错误:
GCC v6.1 compiles it, clang 3.8 rejects it with the error:
2:错误:在'U'中没有名为'foo' >
struct S {static constexpr int bar = T :: foo; };
2 : error: no member named 'foo' in 'U'
struct S { static constexpr int bar = T::foo; };
哪个编译器是正确的?
这可能是因为 U
不是完整类型在我们尝试在 S
中使用它的点?
在这种情况下,它应该被认为是GCC的一个错误,但我想要知道我是否正在搜索/打开错误跟踪器上的问题...
Which compiler is right?
Could it be due to the fact that U
is not a complete type at the point where we try to use it within S
?
In this case, it should be considered a bug of GCC, but I'd like to know if I'm right before to search/open an issue on the bug tracker...
编辑
同时我开了一个错误到GCC。
等待它接受答案。
Meanwhile I've opened a bug to GCC.
Waiting for it to accept the answer.
推荐答案
对于C ++ 14和11,Clang是对的;
For C++14 and 11, Clang is right; however, things have changed in the latest working draft (the future C++17) - see the next section.
要查找的标准报价是(从N4140,最接近C ++ 14的草案):
The Standard quotes to look for are (from N4140, the draft closest to C++14):
[temp.inst] / 1:
[temp.inst]/1:
[...]类模板专用化的隐式实例化
导致声明的隐式实例化,但不是
定义,默认参数或异常规范的
类成员函数,成员类,范围成员枚举,
静态数据成员和成员模板; [...]
[...] The implicit instantiation of a class template specialization causes the implicit instantiation of the declarations, but not of the definitions, default arguments, or exception-specifications of the class member functions, member classes, scoped member enumerations, static data members and member templates; [...]
[temp.point] / 4:
[temp.point]/4:
对于类模板专用化,[...]实例化的点
用于这样的专门化紧接在命名空间范围之前
声明或定义引用专门化。 / p>
For a class template specialization, [...] the point of instantiation for such a specialization immediately precedes the namespace scope declaration or definition that refers to the specialization.
因此, S
的实例化点声明 U
,只有一个向前声明 struct U;
在概念上插入之前,因此名称 U
。
So, the point of instantiation for S<U>
is right before the declaration of U
, with only a forward declaration struct U;
conceptually inserted before, so that the name U
is found.
[class.static.data] / 3:
[class.static.data]/3:
[...]可以在
类定义中使用constexpr
说明符声明文字类型的静态数据成员;如果是,其
声明应指定括号或初始值,其中
每个 initializer-clause -expression 是一个
常量表达式。 [...]如果在程序中使用odr(3.2),并且
命名空间范围定义不包含一个初始化器,那么该成员仍然在
命名空间范围中定义。
[...] A static data member of literal type can be declared in the class definition with the
constexpr
specifier; if so, its declaration shall specify a brace-or-equal-initializer in which every initializer-clause that is an assignment-expression is a constant expression. [...] The member shall still be defined in a namespace scope if it is odr-used (3.2) in the program and the namespace scope definition shall not contain an initializer.
根据上面引用的段落, bar
在 S
的定义内,即使它具有初始化器,仍然只是一个声明,而不是一个定义,因此当 S< U>
被隐式实例化,并且当时没有 U :: foo
。
According to the paragraph quoted above, the declaration of bar
within the definition of S
, even though it has an initializer, is still just a declaration, not a definition, so it's instantiated when S<U>
is implicitly instantiated, and there's no U::foo
at that time.
一个解决方法是使 bar
一个函数;根据第一引号,函数的定义不会在隐式实例化 S
时被实例化。只要在已经看到 U
的定义之后(或者从其他成员函数的主体内部)使用 bar
S
,因为那些,反过来,只需要在需要时单独实例化 - [14.6.4.1p1]),这样的东西将工作:
A workaround is to make bar
a function; according to the first quote, the function's definition will not be instantiated at the time of the implicit instantiation of S<U>
. As long as you use bar
after the definition of U
has been seen (or from within the bodies of other member functions of S
, since those, in turn, will only be instantiated separately when needed - [14.6.4.1p1]), something like this will work:
template<class T> struct S
{
static constexpr int bar() { return T::foo; }
};
struct U : S<U> { static constexpr int foo = 42; };
int main()
{
constexpr int b = U::bar();
static_assert(b == 42, "oops");
}
a href =http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0386r2.pdf =nofollow> P0386R2 加入工作草案(目前< a href =http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/n4606.pdf =nofollow> N4606 ),[class.static。数据] / 3;相关部分现在为:
Following the adoption of P0386R2 into the working draft (currently N4606), [class.static.data]/3 has been amended; the relevant part now reads:
[...]内联静态数据成员可以在类定义
中定义,可以指定 brace-or-equal-initializer 。如果成员是使用constexpr
说明符声明的
,它可以在
命名空间范围中重新声明,没有初始化器(此用法已弃用;请参阅
D.1)。 [...]
[...] An inline static data member may be defined in the class definition and may specify a brace-or-equal-initializer. If the member is declared with the
constexpr
specifier, it may be redeclared in namespace scope with no initializer (this usage is deprecated; see D.1). [...]
这是对[basic.def] /2.3的修改:
This is complemented by the change to [basic.def]/2.3:
声明是定义,除非:
[...]
A declaration is a definition unless:
[...]
- 它在类定义(9.2,9.2.3)中声明了非内联静态数据成员,
[...]
因此,如果它是内联的,它是一个定义)。和[dcl.constexpr] / 1说:
So, if it's inline, it's a definition (with or without an initializer). And [dcl.constexpr]/1 says:
[...] c> constexpr
说明符是隐式的内联函数或变量(7.1.6)。 [...]
[...] A function or static data member declared with the
constexpr
specifier is implicitly an inline function or variable (7.1.6). [...]
这意味着 bar
的声明现在是定义,并且根据前面部分中的引用,不会实例化 S
的隐式实例化;只有 bar
的声明(不包括初始化程序)将在此时实例化。
Which means the declaration of bar
is now a definition, and according to the quotes in the previous section it's not instantiated for the implicit instantiation of S<U>
; only a declaration of bar
, which doesn't include the initializer, is instantiated at that time.
在这种情况下,在当前工作草案中的[depr.static_constexpr]的示例中很好地总结:
The changes in this case are nicely summarized in the example in [depr.static_constexpr] in the current working draft:
struct A {
static constexpr int n = 5; // definition (declaration in C++ 2014)
};
const int A::n; // redundant declaration (definition in C++ 2014)
这使得GCC的行为与C ++ 1z模式。
This makes GCC's behaviour standard-conformant in C++1z mode.
这篇关于通过使用派生类的静态constexpr数据成员来初始化基类的静态constexpr数据成员的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!