C++ 对静态类成员的未定义引用
有人能解释为什么下面的代码无法编译吗?至少在g++4.2.4上 更有趣的是,当我将成员转换为int时,为什么它会编译C++ 对静态类成员的未定义引用,c++,g++,C++,G++,有人能解释为什么下面的代码无法编译吗?至少在g++4.2.4上 更有趣的是,当我将成员转换为int时,为什么它会编译 #include <vector> class Foo { public: static const int MEMBER = 1; }; int main(){ vector<int> v; v.push_back( Foo::MEMBER ); // undefined reference t
#include <vector>
class Foo {
public:
static const int MEMBER = 1;
};
int main(){
vector<int> v;
v.push_back( Foo::MEMBER ); // undefined reference to `Foo::MEMBER'
v.push_back( (int) Foo::MEMBER ); // OK
return 0;
}
#包括
类Foo{
公众:
静态常量int成员=1;
};
int main(){
向量v;
v、 向后推(Foo::MEMBER);//对'Foo::MEMBER'的未定义引用
v、 向后推((int)Foo::MEMBER);//确定
返回0;
}
您需要在某个地方(在类定义之后)实际定义静态成员。试试这个:
class Foo { /* ... */ };
const int Foo::MEMBER;
int main() { /* ... */ }
这应该去掉未定义的引用。
< P>问题是因为新C++特性的有趣冲突和你想做的事情。首先,让我们看一下<代码> PuxSuff< <代码>签名:void push_back(const T&)
它期望引用类型为T
的对象。在旧的初始化系统中,存在这样一个成员。例如,以下代码编译得很好:
#include <vector>
class Foo {
public:
static const int MEMBER;
};
const int Foo::MEMBER = 1;
int main(){
std::vector<int> v;
v.push_back( Foo::MEMBER ); // undefined reference to `Foo::MEMBER'
v.push_back( (int) Foo::MEMBER ); // OK
return 0;
}
但是没有预处理器宏的麻烦(并且具有类型安全性)。这意味着需要引用的向量无法获得引用。不知道为什么强制转换可以工作,但是Foo::MEMBER直到第一次加载Foo时才被分配,而且因为您从未加载它,所以它从未被分配。如果你在某个地方引用了一个FO,它可能会起作用。 < P>如果需要某种定义,C++标准需要定义一个静态const成员。 定义是必需的,例如,如果使用它的地址
push_back
通过const引用获取其参数,因此严格来说,编译器需要成员的地址,您需要在名称空间中定义它
当您显式地强制转换常量时,您正在创建一个临时变量,该临时变量绑定到引用(根据标准中的特殊规则)
这是一个非常有趣的案例,我真的认为有必要提出一个问题,以便将std更改为与您的固定成员具有相同的行为
虽然,以一种奇怪的方式,这可能被视为一元“+”运算符的合法使用。基本上,一元数+
的结果是一个右值,因此将右值绑定到常量引用的规则适用,我们不使用静态常量成员的地址:
v.push_back( +Foo::MEMBER );
Aaa.h
class Aaa {
protected:
static Aaa *defaultAaa;
};
Aaa.cpp
// You must define an actual variable in your program for the static members of the classes
static Aaa *Aaa::defaultAaa;
关于第二个问题:push_ref将引用作为参数,并且不能引用类/结构的static const memeber。调用static_cast后,将创建一个临时变量。可以传递对这个对象的引用,一切正常
或者至少我解决这个问题的同事是这么说的。使用C++11,基本类型也可以实现上述功能
class Foo {
public:
static constexpr int MEMBER = 1;
};
constexpr
部分创建了一个与静态变量相反的静态表达式,其行为就像一个非常简单的内联方法定义。不过,这种方法在模板类中使用C字符串constexpr时有点不稳定 在C++17中,使用inline
变量有一个更简单的解决方案:
structfoo{
内联静态int成员;
};
这是
成员的定义,而不仅仅是其声明。与内联函数类似,不同翻译单元中的多个相同定义不会违反ODR。不再需要为定义选择一个最喜欢的.cpp文件。好的一点是,内联静态常量整数初始化创建一个范围内的整数常量,您不能获取该常量的地址,而vector获取一个引用参数。这个答案只解决了问题的第一部分。第二部分更有趣:为什么添加NOP cast可以在不需要外部声明的情况下工作?我花了很长时间才弄明白,如果类定义在头文件中,那么静态变量的分配应该在实现文件中,不是头球。@shanet:说得很好——我应该在回答中提到这一点!但是,如果我将其声明为const,那么我不可能更改该变量的值吗?我编辑了这个问题,将代码缩进四个空格,而不是使用。这意味着尖括号不会被解释为HTML。您可以参考这个问题。因为C++17不需要额外的定义,请参见下面的+1。是的,对于类型为T的对象x,表达式“(T)x”可以用来绑定const ref,而普通的“x”不能。我喜欢你对“一元+”的观察!谁会想到可怜的小“一元+”真的有用处……:)想想一般情况。。。在C++中是否有其他类型的对象,它只有当它已经被定义时,它(1)可以被用作LValk,但是(2)可以被转换成一个没有定义的R值,好的问题,至少目前我不能想到任何其他的例子。这可能只是因为委员会主要是在重复使用现有的语法。@RichardCorden:一元+如何解决这个问题?@Blood HaZaRd:在右值引用之前,push_back
的唯一过载是一个const&
。使用该成员直接导致该成员绑定到引用,这要求该成员具有地址。但是,添加+
会创建一个带有成员值的临时值。然后,该引用将绑定到该临时引用,而不是要求成员拥有地址。谢谢,这有助于。。。这可能符合条件,如果它还没有…还值得注意的是,MSVC接受非演员版本没有投诉。-1:这根本不是真的。当odr在某个地方使用时,仍然应该定义内联初始化的静态成员。编译器优化可能会消除链接器错误,但这不会改变这一点。在这种情况下,左值到右值的转换(由于(int)
cast)发生在translate中
class Foo {
public:
static constexpr int MEMBER = 1;
};