C ++ Nifty Counter习惯用法;为什么? [英] C++ Nifty Counter idiom; why?

查看:78
本文介绍了C ++ Nifty Counter习惯用法;为什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我最近遇到了漂亮的计数器成语.我的理解是,它用于在cout,cerr等标准库中实现全局变量.由于专家选择了它,因此我认为它是一种非常强大的技术.

I recently came across the Nifty Counter Idiom. My understanding is that this is used to implement globals in the standard library like cout, cerr, etc. Since the experts have chosen it, I assume that it's a very strong technique.

我正在尝试了解使用像Meyer Singleton这样的东西有什么好处.

I'm trying to understand what the advantage is over using something more like a Meyer Singleton.

例如,一个人可能只是在头文件中:

For example, one could just have, in a header file:

inline Stream& getStream() { static Stream s; return s; }
static Stream& stream = getStream();

优点是您不必担心引用计数,新的放置或有两个类,即代码简单得多.由于未通过这种方式完成,因此我确定是有原因的:

The advantage is you don't have to worry about reference counting, or placement new, or having two classes, i.e. the code is much simpler. Since it's not done this way, I'm sure there's a reason:

  1. 是否不能保证在共享库和静态库中只有一个全局对象?似乎ODR应该保证只能有一个静态变量.
  2. 是否存在某种性能成本?似乎在我的代码和Nifty Counter中,您正在遵循一个引用来访问该对象.
  3. 在某些情况下引用计数实际上有用吗?如果包含头文件,似乎仍会导致对象被构造,并在程序结束时将其销毁,例如Meyer Singleton.
  4. 答案是否涉及dlopen手动操作?我对此没有太多经验.

在阅读Yakk的答案时,系统提示我编写以下代码,我将其添加到原始问题中作为快速演示.这是一个非常简单的示例,显示了使用Meyer Singleton +全局引用如何在main之前进行初始化: http://coliru.stacked-crooked.com/a/a7f0c8f33ba42b7f .

I was prompted to write the following bit of code while reading Yakk's answer, I add it to the original question as a quick demo. It's a very minimal example that shows how using the Meyer Singleton + a global reference leads to initialization before main: http://coliru.stacked-crooked.com/a/a7f0c8f33ba42b7f.

推荐答案

静态本地/Meyer的单例+静态全局引用(您的解决方案)几乎等同于漂亮的计数器.

The static local/Meyer's singleton + static global reference (your solution) is nearly equivalent to the nifty counter.

区别如下:

  1. 您的解决方案中不需要.cpp文件.

  1. No .cpp file is required in your solution.

技术上,每个编译单元中都存在 static Steam& .所引用的对象没有.由于在当前版本的C ++中无法检测到此问题,因此在这种情况下就消失了.但是某些实现可能实际上创建了该引用,而不是将其省略.

Technically the static Steam& exists in every compilation unit; the object being referred to does not. As there is no way to detect this in the current version of C++, under as-if this goes away. But some implementations might actually create that reference instead of eliding it.

有人可以在创建 static Stream& 之前调用 getStream();这将导致销毁顺序困难(流被销毁的时间比预期的晚).可以通过违反规则来避免这种情况.

Someone could call getStream() prior to the static Stream& being created; this would cause difficulty in destruction order (with the stream being destroyed later than expected). This can be avoided by making that against the rules.

该标准旨在确保在 inline getStream 线程中本地创建静态流的安全性.对于编译器来说,检测到这种情况不会发生是具有挑战性的,因此解决方案中可能存在一些多余的线程安全开销.漂亮的计数器明确支持线程安全.这是安全的,因为它在预期线程之前的静态初始化时间运行.

The standard is mandated to make creating the static Stream local in the inline getStream thread safe. Detecting that this is not going to happen is challenging for the compiler, so some redundant thread-safety overhead may exist in your solution. The nifty counter does not support thread safety explicitly; this is considered safe as it runs at static initialization time, prior to when threads are expected.

getStream()的调用必须在每个编译单元中进行.只有证明它无能为力,才能对其进行优化,这很困难.精巧的计数器具有相似的成本,但是操作可能会或可能不会更简单以优化输出或运行时成本.(要确定这一点,需要在各种编译器上检查生成的程序集输出)

The call to getStream() must occur in each and every compilation unit. Only if it is proven that it cannot do anything may it be optimized out, which is difficult. The nifty counter has a similar cost, but the operation may or may not be be simpler to optimize out or in runtime cost. (Determining this will require inspecting resulting assembly output on a variety of compilers)

在C ++ 11中引入的魔术静态变量"(没有竞争条件的本地静态变量).您的代码在C ++ 11魔术静态之前可能还有其他问题;我唯一能想到的就是有人在静态初始化期间直接在另一个线程中直接调用 getStream(),这通常应该被禁止(如上所述).

"magic statics" (statics locals without race conditions) where introduced in C++11. There could be other issues prior to C++11 magic statics with your code; the only one I can think of is someone calling getStream() directly in another thread during static initialization, which (as mentioned above) should be banned in general.

在标准领域之外,您的版本将自动神奇地在每个动态链接的代码块(DLL,.so等)中创建一个新的单例.漂亮的计数器只会在cpp文件中创建单例.这可能使库作者可以更严格地控​​制意外产生新的单例的情况.他们可以将其粘贴到动态库中,而不是生成重复项.

Outside the realm of the standard, your version will automatically and magically create a new singleton in each dynamicly linked chunk of code (DLL, .so, etc). The nifty counter will only create the singleton in the cpp file. This may give the library writer tighter control over accidentally spawning new singletons; they can stick it into the dynamic library, instead of spawning duplicates.

避免一个以上的单身有时很重要.

Avoiding having more than one singleton is sometimes important.

这篇关于C ++ Nifty Counter习惯用法;为什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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