对于下面的代码,为什么主要工作中的第一个案例没有重新声明Foo :: bar,而第二个案例需要它?
struct Foo{
static constexpr int bar = 30;
};
//Declaration of Foo::bar outside of struct
constexpr int Foo::bar;
int returnconstexpr(const int& x) { return x; }
int main()
{
//Ok without declaration outside of struct
std::cout << Foo::bar << std::endl;
//Requires declaration outside of struct
std::cout << returnconstexpr(Foo::bar) << std::endl;
//Here static constexpr works as a definition
static constexpr int x = 2;
std::cout << returnconstexpr(x) << std::endl;
return 0;
}
我假设这是因为在第一种情况下,编译器实际上只是粘在该值中,而在第二种情况下,该函数需要一个在没有重新声明的情况下尚不存在的地址。如果是这样,那么我所说的是宣言实际上是一个定义吗?我对此感到困惑,因为类中提供了初始化器,但它没有使它成为定义。例如,第三种情况就可以了。
答案 0 :(得分:1)
我假设这是因为在第一种情况下,编译器 字面上只是坚持价值,而在第二种情况下 函数需要一个没有的地址 重声明。如果是这样,那么我所说的就是 声明实际上是一个定义?
你已经回答了这个问题。静态成员在类之外定义,所以你拥有的是一个定义。将其传递给函数时,地址是必需的,因此您需要定义静态成员。在第一种情况下,编译器只需将Foo::bar
替换为值。
现在将函数签名更改为以下内容:
int returnconstexpr(int x) { return x; }
在上述情况下,您将不再需要该定义。
这个规则是在C ++标准的3.2中:
变量x,其名称显示为可能已评估的表达式 ex是odr-used,除非x是满足要求的对象 出现在常量表达式(5.19)中,ex是一个元素 表达式e的潜在结果集,其中任何一个 左值到左值的转换(4.1)适用于e,或e是a 丢弃值表达式(第5条)。
在上述情况下,立即应用左值到右值的转换,因此它没有使用(如标准所述)并且不需要定义。简单来说,这意味着它可以只使用该值而不需要知道地址,但是当您使用引用类型(const int&amp;)时,需要编译器知道对象在内存中的位置。