對於C++ 14和11,Clang是對的;然而,最新的工作草案(未來的C++ 17)已經發生了變化 - 請參閱下一節。
標準引用查找是(從N4140,最接近C++ 14草案):
[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:
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>
的一點是U
聲明之前正確,只有一個向前聲明struct U;
概念之前插入,從而使名稱U
被發現。
[class.static.data]/3:
[...] 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
。
解決方法是使bar
成爲函數;根據第一個引用,函數的定義將不會在S<U>
的隱式實例化時被實例化。只要您在看到U
的定義後(或從S
的其他成員函數的主體內使用bar
,因爲這些函數反過來只會在需要時單獨實例化 - [14.6.4.1p1]),像這樣將工作:
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");
}
在通過的P0386R2進入工作草案(目前N4606),[class.static。數據]/3已經修改;相關部分現規定:
[...] 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補充:
A declaration is a definition unless:
[...]
- it declares a non-inline static data member in a class definition (9.2, 9.2.3),
[...]
所以,如果是內聯,這是一個定義(有或沒有初始化)。和[dcl.constexpr]/1說:
[...] A function or static data member declared with the constexpr
specifier is implicitly an inline function or variable (7.1.6). [...]
這意味着bar
的聲明現在是一個定義,並根據上一節它不是實例化的S<U>
隱式實例化的報價;只有bar
(不包括初始值設定項)的聲明在當時被實例化。
在這種情況下的變化是很好的總結在[depr.static_constexpr]在當前工作草案的例子:
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模式。
標準工作草案的最新一批更新包括對與*內聯變量*的新概念有關的[9.2.3.2p3]的更改*('constexpr'變量現在隱含*內聯*,就像函數一樣),所以C++ 17的答案可能會改變;目前的仍然適用於C++ 14及更低版本。我將等待最新版本的規範在官方郵件中發佈,然後我將使用C++ 17特定信息更新答案。 – bogdan
@bogdan哇,非常感謝。非常感謝。 – skypjack
答覆已更新。 – bogdan