【问题标题】:Static Cast to access static const class member [duplicate]静态转换以访问静态 const 类成员 [重复]
【发布时间】:2017-10-18 12:24:45
【问题描述】:

所以昨天我正在寻找 SO 并找不到以下问题的答案。这种情况来自我正在使用的一些代码,但这里是演示它的 MCVE。

我在 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_ */

然后在main中我简单地调用B类的构造函数。

#include "B.h"

int main() {

    B b;

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

我的问题是为什么普通强制转换或静态强制转换允许我访问这个尚未前向声明的静态 const 变量。在普通代码中,我会转发声明变量或将其声明为 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

我的主要问题是为什么强制转换有效,而不是解决方案是在 main 或 B.h 中定义 A::test (我知道这是有效的)。我明白这将被接受和适当的。主要问题是关于不被接受的方式,即铸造。在幕后,为什么投射对链接有效?

【问题讨论】:

  • 为什么testVec.push_back(A::test);不起作用?它应该...
  • @Rene 因为静态应该在某处定义并存储在某处
  • @Rene 它不会链接,我已经用我的嵌入式系统的 MULTI 编译器和 Cygwin GCC 尝试了这个。输出:对 `A::test' 的未定义引用
  • 别告诉我。我希望 OP 发布他得到的错误,这样我们就可以解决这个错误。然而,演员表不是正确的解决方案。
  • @Rene 我知道解决方案是前向声明,并且它将与它相关联。但为什么演员阵容有效?这是我的问题。

标签: c++ casting


【解决方案1】:

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

但是,还有一条额外的规则是,如果只使用它的值、不获取其地址并且没有引用绑定到它(类似于取其地址)。

您调用的函数是void std::vector&lt;int&gt;::push_back(const int&amp;);。所以直接传递A::test会将函数参数引用直接绑定到对象上,需要一个定义。

另一方面,如果您传递(const int)A::teststatic_cast&lt;const int&gt;(A::test),这将强制使用A::test 的值创建一个临时的int 值,并且引用将绑定到该临时值。所以在这种情况下,A::test 的定义是不必要的。

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

在此之前,请务必在某个 *.cpp 文件中定义您的所有类 static 成员,以防您确实以需要定义的方式使用它们。

【讨论】:

  • 不错的答案。投我一票。
  • 很好的答案,教会了我很多。谢谢!
  • 感谢您的精彩回答。
【解决方案2】:

你的代码相当于这个简化的1 sn-p:

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): 对 `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;
}

1) 您的示例的重要部分是使用对A::test 的引用(请参阅definition of std::vector::push_back()。这就是为什么我将B 定义为使用对A::value 的引用.

【讨论】:

  • 没关系,我同意你所说的。我的主要问题更多关于为什么演员表有效(我知道这不会被接受的编码标准,但我想知道为什么演员表有效)
  • @9Breaker 铸造创建临时。我正要添加它,但 aschepler 打败了我;)
【解决方案3】:

演员表是不需要的,和问题无关。

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

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

const int A::test;

【讨论】:

  • 是的,但即使包含标头 A.h,它也不会在没有前向声明的情况下链接。铸造将起作用,并且链接良好。或者在 main 或 B.h 中前向声明 const int A::test 也将很好地链接。我的问题是演员如何在没有前向声明的情况下使链接工作?
猜你喜欢
  • 2018-10-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-13
  • 2013-09-25
相关资源
最近更新 更多