更有趣的是,当我将MEMBER转换为int时为什么会编译?
#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;
}
#1 楼
您实际上需要在某个地方(在类定义之后)定义静态成员。尝试以下操作:class Foo { /* ... */ };
const int Foo::MEMBER;
int main() { /* ... */ }
那应该摆脱未定义的引用。
评论
好的一点,内联静态const整数初始化创建了一个带范围的整数常量,您不能使用其地址,而vector则采用一个引用参数。
–埃文·特兰(Evan Teran)
08年7月7日在18:08
该答案仅解决问题的第一部分。第二部分更有趣:为什么添加NOP强制转换使其无需外部声明即可工作?
–布伦特·布拉德本(Brent Bradburn)
2011-2-1在0:48
我花了很多时间弄清楚,如果类定义在头文件中,则静态变量的分配应在实现文件中,而不是头中。
– Shanet
2012年7月14日,下午3:06
@shanet:非常好-我应该在回答中提到这一点!
–德鲁厅
2012年7月14日,下午3:10
但是,如果我将其声明为const,是否无法更改该变量的值?
– Namratha
2012年11月27日4:57
#2 楼
问题的出现是由于新的C ++功能与您要执行的操作发生了有趣的冲突。首先,让我们看一下push_back
签名: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;
}
这是因为在某个实际对象中存储了该值。但是,如果像上面那样切换到指定静态const成员的新方法,则
Foo::MEMBER
不再是对象。它是一个常量,有点类似于:#define MEMBER 1
,但是没有预处理器宏的麻烦(并且具有类型安全性)。这意味着期望参考的向量无法获得一个。
评论
谢谢,这有所帮助...如果尚不存在,则有资格获得stackoverflow.com/questions/1995113/strangest-language-feature ...
–安德烈·霍尔兹纳(Andre Holzner)
2010-12-3 13:45
同样值得注意的是,MSVC接受了非广播版本,没有任何投诉。
– porges
2012年6月26日23:49
-1:这根本不是事实。当在某个地方使用过的静态成员时,仍然应该定义它们以内联方式初始化。编译器的优化可能会消除您的链接器错误,但不会改变它。在这种情况下,您的左值到右值转换(由于(int)强制转换)发生在转换单元中,并且具有完全的常数可见性,并且不再使用Foo :: MEMBER了。这与第一个函数调用相反,在第一个函数调用中,传递引用并在其他位置进行评估。
–轨道轻度竞赛
2014年10月6日在22:12
无效的push_back(const T&value);呢? const&可以与右值绑定。
–科斯塔斯
2月24日21:38
#3 楼
如果某种需要,C ++标准需要为您的静态const成员定义。定义是必需的,例如,如果使用了它的地址。
push_back
通过const引用获取其参数,因此严格来说,编译器需要您成员的地址,并且需要在名称空间中对其进行定义。显式强制转换常量时,您正在创建一个临时变量这是绑定到引用的临时文件(在标准的特殊规则下)。
这是一个非常有趣的案例,我实际上值得提出一个问题,以便将std更改为对您的常任成员具有相同的行为!
尽管如此,但以一种奇怪的方式,这可以被视为一元'+'运算符的合法使用。基本上,
unary +
的结果是一个右值,因此适用了将右值绑定到const引用的规则,并且我们不使用静态const成员的地址:v.push_back( +Foo::MEMBER );
评论
+1。是的,对于类型为T的对象x,可以使用表达式“(T)x”来绑定const ref,而普通的“ x”则不能,这很奇怪。我喜欢您对“一元+”的观察!谁会想到,可怜的小“一元+”实际上有用途... :)
– j_random_hacker
09年5月29日上午10:38
考虑一般情况... C ++中是否还有其他类型的对象具有以下属性:(1)仅当已定义它时才可以将其用作左值,而(2)可以转换为右值而无需定义的?
– j_random_hacker
2009年5月29日10:51
这是一个很好的问题,至少目前我无法想到任何其他示例。这可能只是在这里,因为委员会大多只是在重用现有语法。
–理查德·科登(Richard Corden)
09年5月29日在12:47
@RichardCorden:一元+如何解决这个问题?
–血液HaZaRd
18年6月28日在15:10
@ Blood-HaZaRd:在右值引用之前,push_back的唯一重载是const&。直接使用该成员会导致该成员绑定到引用,这需要它具有地址。但是,加+会创建一个具有成员值的临时值。然后,引用将绑定到该临时对象,而不是要求成员具有地址。
–理查德·科登(Richard Corden)
18年7月13日在10:48
#4 楼
Aaa.hclass 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;
#5 楼
不知道强制转换为什么起作用,但是直到第一次加载Foo时才分配Foo :: MEMBER,并且由于您从不加载它,所以也从未分配它。如果您在某处引用Foo,它可能会起作用。评论
我认为您正在回答自己的问题:强制转换有效,因为它创建了(临时)引用。
– Jaap Versteegh
2011-11-30 15:43
#6 楼
在C ++ 11中,以上对于基本类型都是可能的,如class Foo {
public:
static constexpr int MEMBER = 1;
};
constexpr
部分创建一个与静态变量相对的静态表达式-且其行为仅就像一个非常简单的内联方法定义。不过,这种方法在模板类内部使用C字符串constexprs证明有点不稳定。 评论
这对我来说至关重要,因为“ static const int MEMBER = 1;”在开关中使用MEMBER是必需的,而在向量中使用它则需要外部声明,并且您不能同时拥有两者。但是,您在此处给出的表达式至少对我的编译器都适用。
–本·农夫
19/09/16在15:33
@BenFarmer:这种方法不再需要C ++ 17中的类外定义(因为它是隐式内联的,因为变量可以在该版本中)。
–戴维斯·鲱鱼
11月21日3:06
#7 楼
关于第二个问题:push_ref将引用作为参数,并且您不能具有对类/结构的静态const成员的引用。调用static_cast后,将创建一个临时变量。并且可以传递对该对象的引用,一切正常。或者至少是解决这个问题的我的同事这么说。
评论
我编辑了问题,使代码缩进了四个空格,而不是使用 。这意味着尖括号不会被解释为HTML。stackoverflow.com/questions/16284629 / ...您可以参考此问题。