让我先说明我的意图.在旧的(C++)时代,我们会有如下代码:
class C
{
public:
enum {SOME_VALUE=27};
};
Run Code Online (Sandbox Code Playgroud)
然后我们可以SOME_VALUE在整个代码中使用编译时常量,无论编译器在哪里看C::SOME_VALUE,它都只是插入文字27.
现在,将代码更改为以下内容似乎更为可接受:
class C
{
public:
static constexpr int SOME_VALUE=27;
};
Run Code Online (Sandbox Code Playgroud)
这看起来更清晰,提供SOME_VALUE了一个定义良好的类型,并且似乎是C++ 11中的首选方法.(至少对我来说不可靠)问题是,这也导致SOME_VALUE需要在外部进行的情况.也就是说,在某个地方的某个cpp文件中,我们需要添加:
constexpr int C::SOME_VALUE; // Now C::SOME_VALUE has external linkage
Run Code Online (Sandbox Code Playgroud)
导致这种情况的情况似乎是在使用const引用时SOME_VALUE,这在C++标准库代码中经常发生(请参阅本问题底部的示例).顺便说一句,我使用gcc 4.7.2作为我的编译器.
由于这种困境,我被迫恢复定义SOME_VALUE为枚举(即旧学校),以避免必须为某些(但不是所有)静态constexpr成员变量的cpp文件添加定义.是不是有某种方法告诉编译器这constexpr int SOME_VALUE=27意味着SOME_VALUE应该只将其视为编译时常量而不是具有外部链接的对象?如果您看到与它一起使用的const引用,请创建一个临时引用.如果你看到它的地址,如果需要的话就会产生编译时错误,因为它是编译时常量而已.
以下是一些看似良性的示例代码,它们使我们需要SOME_VALUE在cpp文件中添加定义(再次使用gcc 4.7.2进行测试):
#include <vector>
class C
{
public:
static constexpr int SOME_VALUE=5;
};
int main()
{
std::vector<int> iv;
iv.push_back(C::SOME_VALUE); // Will cause an undefined reference …Run Code Online (Sandbox Code Playgroud) 这可能是有点不寻常的问题,因为它要求给一个简短的回答更充分的说明了另一个问题以及与之相关的C++标准11的某些方面.
为便于参考,我将在此总结引用的问题.OP定义了一个类:
struct Account
{
static constexpr int period = 30;
void foo(const int &) { }
void bar() { foo(period); } //no error?
};
Run Code Online (Sandbox Code Playgroud)
并且想知道为什么他没有得到关于他使用类内初始化静态数据成员的错误(一本书提到这是非法的).Johannes Schaub的回答指出:
尽管我依赖这个答案的来源和有效性,但我真的不喜欢它,因为我个人觉得它太神秘了,所以我试图找出一个更有意义的答案,只取得部分成功.相关似乎是§9.4.2/ 4:
"有应是静态数据成员中的恰好一个定义ODR使用的程序中的(3.2); 没有诊断是必需的 " [重点是矿]
这让我更接近这一点.这就是§3.2/2定义了一个使用了odr的变量:
"名称显示为潜在评估表达式的变量是odr-used,除非它是满足出现在常量表达式(5.19)中的要求的对象,并且立即应用左值到右值转换(4.1)" [重点是我的]
在OP的问题中,变量period显然满足出现在常量表达式中的要求,即constexpr变量.因此必须在第二个条件中找到原因:" 并立即应用左值到右值的转换(4.1) ".
这是我解释标准的麻烦.这第二个条件究竟意味着什么?它涵盖了哪些情况?这是否意味着如果从函数返回静态constexpr变量并非使用 odr(因此可以在类中初始化)?
更一般地说:你可以用静态constexpr变量做什么,这样你可以在课堂上初始化它?
是什么这个 C++ FAQ试图传达?
如果(并且仅当)它具有类外定义,您可以获取静态成员的地址:
class AE {
// ...
public:
static const int c6 = 7;
static const int c7 = 31;
};
const int AE::c7; // definition
int f()
{
const int* p1 = &AE::c6; // error: c6 not an lvalue
const int* p2 = &AE::c7; // ok
// ...
}
Run Code Online (Sandbox Code Playgroud)
然而这编译!
这是基于此处提出的原始问题.
[详细]:以下是评论中要求的相关问题
Lippman在p.303上的c ++引子提到:
class Account {
private:
static constexpr int period = 30;
double daily_tbl[period];
}
Run Code Online (Sandbox Code Playgroud)
如果成员仅在编译器可以替换成员的值的上下文中使用,则不需要单独定义初始化的const或constexpr静态.但是,如果我们在无法替换值的上下文中使用该成员,则必须有该成员的定义.
也:
例如,如果我们将Account :: period传递给一个带有const int&的函数,则必须定义period.
那么为什么传递Account::period给const int&需要period必须定义的函数的函数呢?
知道,这将是非常有帮助的,
在下面,static constexpr成员L在类中初始化A,然后通过值或(通用)引用传递.后者在Clang中失败但在GCC中失败,并且成员/非成员函数的行为略有不同.更详细:
#include <iostream>
using namespace std;
struct A
{
static constexpr size_t L = 4;
template <typename T>
void member_ref(T&& x) { cout << std::forward<T>(x) << endl; }
template <typename T>
void member_val(T x) { cout << x << endl; }
};
template <typename T>
void ref(T&& x) { cout << std::forward<T>(x) << endl; }
template <typename T>
void val(T x) { cout << x << endl; }
int main ()
{
A().member_ref(A::L); …Run Code Online (Sandbox Code Playgroud)