谁能解释为什么以下代码无法编译?至少在g ++ 4.2.4上。

更有趣的是,当我将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;
}


评论

我编辑了问题,使代码缩进了四个空格,而不是使用
   
。这意味着尖括号不会被解释为HTML。

stackoverflow.com/questions/16284629 / ...您可以参考此问题。

#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.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;


#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后,将创建一个临时变量。并且可以传递对该对象的引用,一切正常。

或者至少是解决这个问题的我的同事这么说。