非积分常数

时间:2010-01-28 02:07:42

标签: c++ c++11 constants inline

我想要一个带有非整数常量的头文件,例如一类。注意常量需要是编译时常量。

static const std::string Ten = "10";

这会编译但不可取,因为每个编译单元现在都有自己的Ten副本。

const std::string Ten = "10";

这将编译,但会因为多重定义的Ten的链接器错误而失败。

constexpr std::string Ten = "10"s;

这可以工作,但前提是字符串构造函数也是constexpr。它会是但我不能指望每个非整数常量都有一个constexpr构造函数......或者我可以吗?

extern const std::string Ten = "10";

这似乎有效,但如果我错误地呼吸,我恐怕会收到链接器错误。

inline const std::string Ten( ) { return "10"; }

除了干净的语法之外,这还有我想要的一切。另外,现在我必须将常量称为函数调用Ten()

inline const std::string = "10";

这似乎是理想的解决方案。当然,标准不允许inline个变量。

  • c ++标准中是否有一些内容表明extern版本应该有用,或者我很幸运它与GCC一起使用?
  • 是否有令人信服的理由不允许内联变量?
  • c ++ 03有更好的方法还是c ++ 0x会有更好的方法?

5 个答案:

答案 0 :(得分:26)

你好像混淆了他们。

你是对的

static const std::string Ten = "10"; 

版本。它将“工作”,但它将在每个翻译单元中创建一个单独的对象。

没有static的版本会产生相同的效果。它不会产生链接器错误,但会在每个转换单元中定义一个单独的对象。在C ++语言中,const对象默认具有内部链接,这意味着

const std::string Ten = "10"; // `static` is optional

static的先前版本完全相同。

包含extern和初始化程序

的版本
extern const std::string Ten = "10"; // it's a definition!

将生成具有外部链接的对象的定义(由于存在初始化程序,它是一个定义)。 版本将导致链接器错误,因为您将最终得到具有外部链接的对象的多个定义 - 违反ODR。

以下是您可以这样做的方法:

为了实现您想要实现的目标,您必须在头文件中声明您的常量

extern const std::string Ten; // non-defining declaration

然后在一个且只有一个实现文件

中定义它(使用初始化程序)
extern const std::string Ten = "10"; // definition, `extern` optional

(如果常量预先声明为extern,则定义中的extern是可选的。即使没有明确的extern,它也会定义一个带有外部链接的const对象。)

答案 1 :(得分:10)

我不知道C ++中是否有更好的方法,但C中最好的方法(也适用于C ++)是你列出的方法之一。

有一个单独的编译单元(例如,ten.cpp)只保存数据:

const std::string Ten = "10";

和一个头文件(例如,ten.h)声明它可以在其他地方使用:

extern const std::string Ten;

然后你必须确保任何想要使用它的编译单元包括头文件(例如,ten.h),并且任何想要使用它的可执行文件链接到单独的编译单元(例如,{{ 1}})。

这为您提供了一个变量的副本,可以在任何地方访问。当然,你可以在头文件中将其定义为静态,并且每个编译单元有一个副本。这将简化您需要的文件,静态将确保没有双重定义的符号。但这不是我推荐过的。

我不知道为什么你声明:

  

但是如果我错误地呼吸,我恐怕会收到链接器错误

这是 long 以前的公认实践,如果你想称自己为C ++程序员(没有任何侮辱),你应该知道所有这些事情是如何组合在一起的。

答案 2 :(得分:6)

extern版本接近你想要的版本。这里:

// in the file tenconstant.cpp
const std::string Ten = "10";

// in the file tenconstant.h
extern const std::string Ten;

// in your file
#include "tenconstant.h"

// do stuff with Ten

您需要为链接器定义一次,这是myconstants.cpp的目的,但是在您使用它的任何地方都会声明,这是myconstants.h的目的。对于一个变量来说,这看起来有点笨拙,但对于一个更大的项目,你可能会有一个很好的标题,你可以使用它很多。

答案 3 :(得分:2)

以这种方式创建静态用户定义类型是一个坏主意。当您有多个此类UDT时,无法控制实例化的顺序。这在小型项目中不是问题,但并非所有项目都很小。最好的做法是将静态数据作为所有普通的旧数据类型 - 原始指针 - 并以适当的方式初始化它们,以指向程序启动时所需的实例,或者何时需要它们。这让你掌控一切。

您的问题表明这些类型不需要是编译时常量。如果是这样,并且您有一个多线程程序,则您的对象需要保护其状态不受多个线程的同时访问。如果某些对象不是线程安全的,那么除了对象本身之外,还需要一个互斥对象来保护其状态,并且必须具有相同的链接,并且需要初始化。所有这些使得程序的全局状态变得复杂,这可能是一种不可接受的方式。

答案 4 :(得分:1)

我认为这里的其他答案更好,但是如果您使用标题完成所有操作,您可以使用简单的包装函数有效地inline您的对象(正如您特别要求的那样)。 / p>

inline const std::string &get_ten() {
    static const std::string ten = "10";
    return ten;
}

只有一个string,初始化一次,并且您不需要在头文件之外的任何内容。