2017-10-18 115 views
4

所以昨天我一直在寻找SO,并找不到以下答案。这种情况来自我正在使用的一些代码,但这里是用于演示它的MCVE。静态演员访问静态const类成员

我在A.h中定义了一个类A,它只有一个静态常量。我已经在头文件中初始化了它。

#ifndef A_H_ 
#define A_H_ 
class A { 
public: 
    static const int test = 5; 
    ~A(){}; 
}; 


#endif /* A_H_ */ 

然后我有一个类B需要从类A访问公共静态常量。在这个例子中,它将深度复制值为一个向量。

#ifndef B_H_ 
#define B_H_ 

#include "A.h" 
#include <vector> 
#include <iostream> 

class B { 
private: 
    std::vector<int> testVec; 

public: 
    B(){ 

     testVec.push_back((const int)A::test); 
     //testVec.push_back(static_cast<const int>(A::test)); //Also works 
     //testVec.push_back(A::test); //Doesn't work without forward declaration of const int A::test in main or in this header 
     //(Compiler link error: undefined reference to `A::test') 
     std::cout<< testVec.front() << std::endl; 
    } 

    ~B(){}; 
}; 

#endif /* B_H_ */ 

然后在主我简单的调用B类的构造函数

#include "B.h" 

int main() { 

    B b; 

    return 0; 
} 
//Does the cout from ctor of B and prints 5 to the screen. 

我的问题是,为什么一个正常的铸造或静态铸造允许我访问此静态常量变量尚未向前声明。在正常的代码中,我会转发声明该变量或将其声明为extern,因为它已经被定义。什么原因为什么演员让我在没有前向声明的情况下访问这个变量? (这可能看起来像一个简单的问题,可能有一个简单的答案,但我想在这里进一步了解我的知识)。

从编译链接错误输出是:

Invoking: Cygwin C++ Linker 
g++ -o "S_Test_p1.exe" ./src/S_Test_p1.o 
./src/S_Test_p1.o:S_Test_p1.cpp:(.rdata$.refptr._ZN1A4testE[.refptr._ZN1A4testE]+0x0): undefined reference to `A::test' 
collect2: error: ld returned 1 exit status 
make: *** [makefile:47: S_Test_p1.exe] Error 1 

我这里主要的问题是,为什么铸造的作品,而不是该解决方案是主要还是在波黑定义A ::测试(我知道作品)。我明白这将被接受和正确。主要问题是关于未接受的方式,即铸造。在幕后为什么投射工作链接?

+2

为什么'testVec.push_back(A :: test);'不工作?它应该... – Rene

+0

@Rene,因为静态应该在某处定义并存储在某个地方 –

+0

@Rene它不会链接,我已经用我的嵌入式系统和Cygwin GCC的MULTI编译器试过了。输出:未定义的参考'A :: test' – 9Breaker

回答

7

static const int test = 5;类中static成员的声明是声明,但不是定义,即使它具有初始值设定项。声明通常应该有相应的定义。这个定义看起来像是const int A::test;(它不是“前向声明”)。

但是,还有一个额外的规则,即如果仅使用其值,则不需要定义整型类成员,其地址是没有被采纳,并且没有任何参考文献可以与之相关(这与采取地址相似)。

你打电话的功能是void std::vector<int>::push_back(const int&);。所以直接传递A::test会直接将函数参数引用绑定到对象,并需要定义。

在另一方面,如果传递(const int)A::teststatic_cast<const int>(A::test),这迫使使用的A::test值来创建临时int值,和所述参考结合到该临时代替。所以在这种情况下,A::test的定义是不必要的。

注意在C++ 17中,在任何情况下都不需要定义A::test,因为在类定义中带有初始值设定项的static类成员隐含地是inline变量和定义。

在此之前,一定要在某些* .cpp文件中定义所有类static成员,以防您以某种需要定义的方式使用它们。

+0

好的答案。有我的投票。 – YSC

+0

很好的答案,并教会了我很多。谢谢! – 9Breaker

+0

谢谢你的回答。 –

0

演员阵容是不需要的,而且与问题无关。

位于main顶端的#include "B.h"表示包含文件"B.h"的内容。完成之后,#include "A.h""B.h"表示包含文件"A.h"的内容。 "A.h"的定义为A,这就是编译器知道A::test的方式。不需要前向声明(事实上,你不能有一个类成员的前向声明),因为编译器已经看到了类的完整定义。

链接问题是因为你还必须在定义某处的静态成员。通常,您将拥有一个.cpp文件,该文件具有该类的成员函数的定义。该文件还应该定义静态成员:

const int A::test; 
+0

是的,但即使包含标题A.h,它也不会在没有前向声明的情况下链接。铸造将工作,它链接很好。或者在main或B.h中的const int A :: test的前向声明也会很好地链接。我的问题是演员如何在没有前向声明的情况下进行链接工作? – 9Breaker

1

您的代码就相当于这个简化片段:

struct A { static const int value = 5; }; 
struct B { const int& n ; B() : n(A::value) {} }; 

int main() 
{ 
    B b; 
} 

因为A::value这是形成不良的未定义(仅申报)。我的编译器报告一个链接错误:

main.cpp:(.text._ZN1BC2Ev[_ZN1BC5Ev]+0xf): undefined reference to `A::value'

一个解决办法是正确地定义它:

struct A { static const int value = 5; }; 
struct B { const int& n ; B() : n(A::value) {} }; 
const int A::value; 

int main() 
{ 
    B b; 
} 

)你的榜样的重要组成部分,是一个参考的使用A::test(见definition of std::vector::push_back()。这就是为什么我定义B使用对A::value的引用。

+0

这没什么,我同意你所说的话。我的主要问题是更多的关于为什么铸造作品(我知道不会被接受的编码标准,但我想知道为什么铸造作品) – 9Breaker

+1

@ 9Breaker铸造暂时创建。我正准备添加它,但abcheler打败了我;) – YSC