2015-09-27 87 views
8

我对staticconst成员的课程初始化有点困惑。例如,在下面的代码:类中静态常量ODR

#include <iostream> 

struct Foo 
{ 
    const static int n = 42; 
}; 

// const int Foo::n; // No ODR 

void f(const int& param) 
{ 
    std::cout << param << std::endl; 
} 

int g(const int& param) 
{ 
    return param; 
} 

template<int N> 
void h() 
{ 
    std::cout << N << std::endl; 
} 

int main() 
{ 
    // f(Foo::n); // linker error, both g++/clang++ 
    std::cout << g(Foo::n) << std::endl; // OK in g++ only with -O(1,2 or 3) flag, why?! 
    h<Foo::n>(); // this should be fine 
} 

Live example

我不限定Foo::n(线被注释)。所以,我预计电话f(Foo::n)在链接时失败,事实上它确实如此。但是,每当我使用优化标志(如-O1/2/3)时,以下行std::cout << g(Foo::n) << std::endl;仅由gcc编译和链接(clang仍会发出链接器错误)。

  1. 为什么海湾合作委员会(试图用gcc5.2.0和gcc 4.9.3)编译和链接代码时优化是否已经开启?
  2. 我正确地说,在课堂上静态const成员的唯一用法是在常量表达式,如模板参数如h<Foo::n>调用,在这种情况下代码应链接?
+2

[odr违规不需要诊断](http://stackoverflow.com/a/28446388/1708801)。 –

+0

这是专门针对C++ 11的问题吗? odr引号[在C++ 11和C++ 14之间改变了一点](http://stackoverflow.com/a/28846608/1708801),虽然我不认为它在这种情况下确实很重要,但在某些情况下它确实。 –

+0

@ShafikYaghmour我用'-std = C++ 11'编译,但问题本身不一定只用C++ 11(或C++ 14)。 O也观察到与'-std = C++ 98'相同的行为。 – vsoftco

回答

4

ODR违规不需要C++标准草案部分草案3.2 [basic.def。ODR(重点矿山前进):

每个程序应包括每一个非内嵌 函数或变量,它是ODR使用的在该程序中的恰好一个定义; 无诊断 需要

因此,不同优化级别的不一致行为是完全一致的行为。

非正式的变量是odr-used如果:

其地址被占用,或引用绑定到它,并且如果一个函数调用它是由一个功能是ODR使用或它的地址取。如果一个对象或函数被使用了,它的定义必须存在于程序中的某个地方;违反该规定的是链接时间错误。

因此,fg将odr用途和需要定义。

上ODR使用相关C++ 14引用将是从节[basic.def.odr]

出现一个变量x,其名称为潜在评估表达EX是odr-由ex使用除非应用 左值到右值转换(4.1)到x产生不调用任何不平凡 函数的常量表达式(5.19),如果x是对象,则ex为的元素表达式e的潜在结果集 其中左值到右值转换(4.1)被施加到E,或e为丢弃值表达式 [...]

措辞在C++ 11是相似的,从C++ 11到C++ 14的变化反映在defect report 712

在C++ 11之前它是a bit more complicated but in principle the same for this case

2

根本没有定义。 GCC 4.9.2不会编译和链接任何标志。

注意,即:

const static int n = 42; 

声明初始,但不是定义

+0

我知道这不是定义,但正如您从现场示例中所看到的,代码已编译并链接。 – vsoftco

+0

我尝试了GCC 4.9.2,但没有。 – Nevermore

+0

这很奇怪。我没有gcc4.9.2,但在OS X上有macintosh的4.9.3,它编译和链接。 – vsoftco

4

我想,编译器优化过程中执行以下操作:

  • const static int n到处内联。没有内存分配给变量n,对其的引用变为无效。函数f()需要对n的引用,因此程序未编译。

  • 功能g简短。它被有效地内联和优化。优化后,将函数g不需要n的引用,它只是返回恒定值42.

的解决方案是定义类以外的变量:

struct Foo 
{ 
    const static int n; 
}; 

const int Foo::n = 42; 
+0

这是有道理的。 – vsoftco

+0

替代解决方案是使它成为constexpr或枚举值。一个'constexpr'可以说比较好,现在编译器一般都支持它,因为它的源代码较少,并且它在头文件中很好地工作。 –

4

形式上,ODR违规是未定义的行为,所以编译器可能会表现出它喜欢的任何行为。这就是为什么行为随着优化级别和编译器而变化的原因 - 编译器没有义务维护特定的行为。