C ++ 11中的全局常量 [英] Global constants in C++11

查看:99
本文介绍了C ++ 11中的全局常量的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在C ++中声明和定义全局常量的最佳方法是什么?我对C ++ 11标准最感兴趣,因为它在这方面已经解决了很多。

What are the best ways to declare and define global constants in C++? I am mostly interested in C++11 standard as it fixes a lot in this regard.

:在此问题中,全局常量表示常量在 any 范围内编译时已知的变量或函数。必须可以从多个翻译单元访问全局常数。它不一定是constexpr样式的常量-可以像 const std :: map< int,std :: string> m = {{1, U},{5, V}}; const std :: map< int,std :: string> * mAddr(){return& m; } 。在此问题中,我不会触摸首选的良好风格范围或常量名称。让我们将这些问题留给另一个问题。 [END_EDIT]

: in this question "global constant" denotes constant variable or function that is known at compile time in any scope. Global constant must be accessible from more than one translation unit. It is not necessarily constexpr-style constant - can be something like const std::map<int, std::string> m = { { 1, "U" }, { 5, "V" } }; or const std::map<int, std::string> * mAddr() { return & m; }. I do not touch preferable good-style scope or name for constant in this question. Let us leave these matters for another question. [END_EDIT]

我想知道所有不同情况的答案,所以让我们假设 T 是一个以下内容之一:

I want to know answers for all the different cases, so let us assume that T is one of the following:

typedef    int                     T;  // 1
typedef    long double             T;  // 2
typedef    std::array<char, 1>     T;  // 3
typedef    std::array<long, 1000>  T;  // 4
typedef    std::string             T;  // 5
typedef    QString                 T;  // 6
class      T {
   // unspecified amount of code
};                                     // 7
// Something special
// not mentioned above?                // 8

我相信没有很大的语义(我确实这里不讨论良好的命名或作用域样式)三种可能的作用域之间的区别:

I believe that there is no big semantic (I do not discuss good naming or scope style here) difference between the 3 possible scopes:

// header.hpp
extern const T tv;
T tf();                  // Global
namespace Nm {
    extern const T tv;
    T tf();              // Namespace
}
struct Cl {
    static const T tv;
    static T tf();       // Class
};

但是如果从以下替代方法中选择更好的方法取决于上述声明范围之间的差异,请指出

But if choosing better way from alternatives below depends on the difference between above declaration scopes, please point it out.

还要考虑在常量定义中使用函数调用的情况,例如<某些值> == f();

Consider also the case when function call is used in constant definition, e.g. <some value>==f();. How would calling a function in constant initialization influence choosing between alternatives?


  1. 让我们考虑 T code>首先使用 constexpr 构造函数。
    显而易见的替代方法是:

  1. Let us consider T with constexpr constructor first. Obvious alternatives are:

// header.hpp
namespace Ns {
constexpr T A = <some value>;
constexpr T B() { return <some value>; }
inline const T & C() { static constexpr T t = <some value>; return t; }
const T & D();
}

// source.cpp
const T & Ns::D() { static constexpr T t = <some value>; return t; }

我相信 A B 最适合小型的 T (例如,有多个实例或在运行时复制它不是问题),例如 1-3 ,有时是 7 。如果 T 大,例如 C D 更好。 4 ,有时是 7

I believe that A and B are most suitable for small T (such that having multiple instances or copying it at runtime is not a problem), e.g. 1-3, sometimes 7. C and D are better if T is large, e.g. 4, sometimes 7.

T 没有 constexpr 构造函数。替代方法:

T without constexpr constructor. Alternatives:

// header.hpp
namespace Ns {
extern const T a;
inline T b() { return <some value>; }
inline const T & c() { static const T t = <some value>; return t; }
const T & d();
}

// source.cpp
extern const T Ns::a = <some value>;
const T & Ns::d() { static const T t = <some value>; return t; }

我通常不使用 a ,因为静态初始化顺序失败。据我所知, b c d 自C ++ 11起是完全安全的,甚至是线程安全的。除非 T 具有非常便宜的构造函数,否则 b 似乎不是一个好选择,这对于非constexpr来说并不常见。构造函数。我可以说 c 优于 d 的一个优点-没有函数调用(运行时性能); d 优于 c 的一个优点-常数值更改时较少进行重新编译(这些优点也适用于 C D )。我确信我在这里错过了很多推理。请在回答中提供其他注意事项。

I would not normally use a because of static initialization order fiasco. As far as I know, b, c and d are perfectly safe, even thread-safe since C++11. b does not seem to be a good choice unless T has a very cheap constructor, which is uncommon for non-constexpr constructors. I can name one advantage of c over d - no function call (run-time performance); one advantage of d over c - less recompiling when constant's value is changed (these advantages also apply to C and D). I am sure that I missed a lot of reasoning here. Provide other considerations in answers please.

如果要修改/测试上述代码,可以使用我的测试文件(只是header.hpp,具有上述代码片段的可编译版本的source.cpp以及可从header.hpp打印常量的main.cpp): https://docs.google.com/uc?export=download&id=0B0F-aqLyFk_PVUtSRnZWWnd4Tjg

If you want to modify / test the above code, you can use my test files (just header.hpp, source.cpp with compilable versions of above code fragments and main.cpp that prints constants from header.hpp): https://docs.google.com/uc?export=download&id=0B0F-aqLyFk_PVUtSRnZWWnd4Tjg

推荐答案


我相信以下声明位置之间没有太大区别:

I believe that there is no big difference between the following declaration locations:

这在很多方面都是错误的。

This is wrong in a lot of ways.

第一个声明污染了全局名称空间;您已不再使用 tv这个名称,而不会造成误解。这可能会引起阴影警告,可能导致链接器错误,并且可能给使用您的标头的任何人造成各种混乱。通过与其他碰巧也使用您的变量名作为全局变量的人发生冲突,这也会给不使用您的标头的人造成问题。

The first declaration pollutes the global namespace; you have taken the name "tv" from ever being used again without the possibility of misunderstandings. This can cause shadowing warnings, it can cause linker errors, it can cause all sorts of confusion to anyone who uses your header. It can also cause problems to someone who doesn't use your header, by causing a collision with someone else who also happens to use your variable name as a global.

在现代C ++中不推荐使用这种方法,但在C语言中却普遍使用这种方法,因此导致对.c文件(文件范围)中的全局变量大量使用static关键字。

Such an approach is not recommended in modern C++, but is ubiquitous in C, and therefore leads to much use of the static keyword for "global" variables in a .c file (file scope).

第二个声明污染名称空间;这几乎没有什么问题,因为名称空间可以自由重命名,并且可以免费创建。只要两个项目使用它们自己的,相对特定的名称空间,就不会发生冲突。在确实发生这种冲突的情况下,可以重命名每个名称空间以避免任何问题。

The second declares pollutes a namespace; this is much less of an issue, as namespaces are freely renamable and can be made at no cost. As long as two projects use their own, relatively specific namespace, no collisions will occur. In the case where such collisions do occur, the namespaces for each can be renamed to avoid any issues.

这是更现代的C ++ 03样式和C + +11通过重命名模板大大扩展了该策略。

This is more modern, C++03 style, and C++11 expands this tactic considerably with renaming of templates.

第三种方法是结构而不是类;它们之间存在差异,特别是如果您想保持与C的兼容性。您不仅可以轻松地封装多个事物并使用特定的名称,还可以通过方法和信息隐藏来增加封装,从而大大扩展了代码的实用性。不管范围收益如何,这都是类的收益。

The third approach is a struct, not a class; they have differences, especially if you want to maintain compatibility with C. The benefits of a class scope compound on the namespace scope; not only can you easily encapsulate multiple things and use a specific name, you can also increase encapsulation via methods and information hiding, greatly expanding how useful your code is. This is mostly the benefit of classes, irrespective of scoping benefits.

几乎可以肯定不使用第一个,除非您的函数和变量非常广泛且STL / STD例如,或者您的程序很小,不可能被嵌入或重复使用。

You should almost certainly not use the first one, unless your functions and variables are very broad and STL/STD like, or your program is very small and not likely to be embedded or reused.

现在让我们看一下您的案例。

Let's now look at your cases.


  1. 构造函数的大小(如果返回常量表达式)并不重要;所有代码都应在编译时可执行。这意味着复杂性没有意义。它将始终编译为单个恒定的返回值。您几乎应该确定永远不要使用C或D。所有要做的就是使constexpr的优化无效。我将使用A和B中看起来更优雅的任何一个,可能简单的赋值为A,复杂的常量表达式为B。

  1. The size of the constructor, if it returns a constant expression, is unimportant; all of the code ought to be executable at compile time. This means the complexity is not meaningful; it will always compile to a single, constant, return value. You should almost certainly never use C or D; all that does is make the optimizations of constexpr not work. I would use whichever of A and B looks more elegant, probably a simple assignment would be A, and a complex constant expression would be B.

这些都不是必须是线程安全的;构造函数的内容将确定线程和异常安全,并且很容易使这些语句中的任何一个都不是线程安全的。实际上,A最有可能是线程安全的。只要在调用main之前不访问该对象,就应该完整地形成该对象;您的任何其他示例都无法做到这一点。至于您对B的分析,以我的经验来看,大多数构造函数(尤其是异常安全的那些)都很便宜,因为它们避免分配。在这种情况下,您的任何情况之间的差异都不大。

None of these are necessarily thread safe; the content of the constructor would determine both thread and exception safety, and it is quite easy to make any of these statements not thread safe. In fact, A is most likely to be thread safe; as long as the object is not accessed until main is called, it should be fully formed; the same cannot be said of any of your other examples. As for your analysis of B, in my experience, most constructors (especially exception safe ones) are cheap as they avoid allocation. In such cases, there's unlikely to be much difference between any of your cases.

我强烈建议您停止尝试这样的微观优化,也许对C ++习惯用法有了更扎实的理解。您在此处尝试执行的大多数操作都不太可能导致性能提高。

I would highly recommend you stop attempting micro-optimizations like this and perhaps get a more solid understanding of C++ idioms. Most of the things you are trying to do here are unlikely to result in any increase in performance.

这篇关于C ++ 11中的全局常量的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆