通过使用派生类的静态constexpr数据成员来初始化基类的静态constexpr数据成员 [英] Initializing a static constexpr data member of the base class by using a static constexpr data member of the derived class

查看:192
本文介绍了通过使用派生类的静态constexpr数据成员来初始化基类的静态constexpr数据成员的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

请考虑以下代码:

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆