[英]non-integral constants
我想要一个带有非整数常量的头文件,例如一个类。 注意常数并不需要是一个编译时间常数。
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
变量。
你好像混淆了他们。
你是对的
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对象。)
我不知道C ++中是否有更好的方法,但C中最好的方法(也适用于C ++)是你列出的方法之一。
有一个单独的编译单元(例如, ten.cpp
)只保存数据:
const std::string Ten = "10";
和一个头文件(例如, ten.h
)声明它,以便它可以在别处使用:
extern const std::string Ten;
然后你必须确保任何想要使用它的编译单元包括头文件(例如, ten.h
),并且任何想要使用它的可执行文件链接到单独的编译单元(例如, ten.o
)。
这为您提供了变量的一个副本,可在任何地方访问。 当然,您可以在头文件中将其定义为静态,并为每个编译单元创建一个副本。 这将简化您需要的文件,静态将确保没有双重定义的符号。 但这不是我推荐过的。
我不知道你为什么说:
但是,如果我错误地呼吸,我恐怕会收到链接器错误
这是很久以前的公认实践,如果你想称自己为C ++程序员(没有任何侮辱),你应该知道所有这些东西是如何组合在一起的。
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
的目的。 对于一个变量来说,这看起来有点笨拙,但是对于一个更大的项目,你可能会有一个很好的标题,可以使用很多,你可以坚持这个。
以这种方式创建静态用户定义类型是一个坏主意。 当您有多个此类UDT时,无法控制实例化的顺序。 这在小型项目中不是问题,但并非所有项目都很小。 最好的做法是将静态数据作为所有普通的旧数据类型 - 原始指针 - 并以适当的方式初始化它们,以指向程序启动时所需的实例,或者何时需要它们。 这让你掌控一切。
您的问题表明这些类型不需要是编译时常量。 如果是这样,并且您有一个多线程程序,则您的对象需要保护其状态不受多个线程的同时访问。 如果某些对象不是线程安全的,那么除了对象本身之外,还需要一个互斥对象来保护其状态,并且必须具有相同的链接,并且需要初始化。 所有这些使得程序的全局状态变得复杂,这可能是一种不可接受的方式。
我认为这里的其他答案更好,但如果您使用标题完成所有操作,您可以使用简单的包装函数有效地inline
您的对象(正如您特别要求的那样)。
inline const std::string &get_ten() {
static const std::string ten = "10";
return ten;
}
将只有一个string
,初始化一次,并且您不需要头文件之外的任何内容。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.