如何声明std :: array的全局编译时常量?

时间:2019-05-06 00:45:42

标签: c++ arrays

我正在尝试实现一个全局变量,该变量可被std :: array同时供不同文件使用,但出现以下编译器错误:

error: the value of ‘constants::HEIGHT’ is not usable in a constant expression

note: ‘constants::HEIGHT’ was not initialized with a constant expression

目前,我的代码分为以下文件:

main.cpp

#include <iostream>
#include "classA.h"
#include "globals.h"

namespace constants {
    extern const int WIDTH = 800;
    extern const int HEIGHT = 600;
}


int main()
{
    ClassA classA;
    printf("Hello World");

    std::cout << constants::WIDTH << " " << constants::HEIGHT << std::endl;
    return 0;
}

classA.h

#include <array>
#include "globals.h"

class ClassA {
public:
    std::array<int, constants::HEIGHT> m_arrayFixedRGBA;

    ClassA();

};

classA.cpp

#include "classA.h"

ClassA::ClassA() {

}

globals.h

#ifndef CONSTANTS_H
#define CONSTANTS_H

namespace constants {
    extern const int WIDTH;
    extern const int HEIGHT;
}

#endif

我知道,通过删除extern,像这样声明globals.h中的值

#ifndef CONSTANTS_H
#define CONSTANTS_H

namespace constants {
    const int WIDTH = 800;
    const int HEIGHT = 600;
}

#endif

并删除main.cpp中的相关行,然后程序即可进行编译。

尽管这很简单(并且适用于较小的程序),但每次globals.h被#include到不同的代码文件中时,这些变量中的每一个都会被复制到包含的代码文件中。因此,如果globals.h被包含在20个不同的代码文件中,则每个变量将重复20次。标头防护不会阻止这种情况的发生,因为它们只会阻止标头被多次包含在一个包含文件中,而不是被一次性包含在多个不同的代码文件中。变量的重复并不是真正的问题(因为常量不太可能很大),但是更改单个常量值还需要重新编译每个包含常量头的文件,这可能会导致较长的重建时间用于大型项目。

在这种情况下,有什么解决方法?

1 个答案:

答案 0 :(得分:1)

您可以将常量定义为static constexpr个成员

// in some header file:
struct constants {
    constexpr static int width  = 800;
    constexpr static int height = 600;
};

并像使用它们

std::array<int, constants::height> arr;

------编辑------

请注意,此方法仅声明这些编译时常量,而不定义任何变量。因此,多个定义不会使链接程序混淆(与使用extern变量的实现一样)。

但是,在C ++ 17之前,可能会出现相反的问题:如果您odr-use这些常量,则将出现链接时错误,因为链接器无法找到定义。例如,以下代码将失败

std::cout << constants::width << std::endl;

因为operator(std::ostream&, something const&)通过引用获取要写入的对象。您可以通过在某处(在源文件中)提供定义或避免使用这种方式来避免这种情况,例如

std::cout << int(constants::width) << std::endl;