【问题标题】:How to ensure std::call_once really is only called once如何确保 std::call_once 真的只被调用一次
【发布时间】:2014-11-27 09:34:28
【问题描述】:

我正在使用的一些代码使用 std::call_once 以便某些初始化只发生一次。但是,有些全局对象的构造函数最终会调用初始化代码。

在下面的示例中, call_once 实际上被调用了两次。我猜这是因为 once_flag 构造函数在使用之前还没有运行。有没有办法让一些初始化代码只被调用一次而不必禁止全局变量?

#include <mutex>
#include <iostream>

using namespace std;

void Init();

class Global
{
public:
    Global()
    {
        Init();
    }
};

Global global;

once_flag flag;

void Init()
{
    call_once(flag, []{  cout << "hello" << endl;  });
}



int main(int argc, char* argv[])
{
    Init();
    return 0;
}

输出是:

hello
hello

【问题讨论】:

  • 对于 GCC 和 Clang,我只看到一次“hello”输出。
  • @remyabel - 嗯,我想知道这是否是 micosoft STL 错误。
  • 您的 using namespace std; 破坏了 C++ 的命名空间机制。它还使您的代码更难理解。不要那样做。而是using std::once_flag;等。即导入特定符号;不要转储命名空间。此外,对于标准 C++ 样式,请避免将函数名大写。

标签: c++ c++11 visual-studio-2013


【解决方案1】:

根据规范,once_flag 应该有一个简单的 constexpr 构造函数(例如,请参见此处 - http://en.cppreference.com/w/cpp/thread/once_flag )。有了这个,如果它是全局/静态的,它实际上并不是“构造的”(没有执行实际的函数),而更像是“值初始化” - 就像任何全局/静态 POD 类型一样。在这种情况下,不可能让任何构造函数在此 once_flag 正确初始化“之前”运行。鉴于您对使用 MSVC 的评论,我想这可能是实现中的一个错误......

编辑: 根据下面的评论,MSVC 根本不支持constexpr,所以你的选择在这里真的很有限......如果你把所有东西都放在一个文件中,只需将你的once_flag“放在”所有使用它的东西上 -文件中的构造函数按照对象声明的顺序执行。如果您的用户分布在不同的文件中,您唯一的选择是使用提供对静态内部once_flag 的访问的函数 - 就像在这个答案中http://www.parashift.com/c++-faq/static-init-order-on-first-use.html 一样。

【讨论】:

  • VS2013 根本不支持constexpr
  • 我想知道静态函数是否会出现问题(因为我的 MSVC 版本不支持魔法静态)在面对多个线程时可能会构造两次 once_flag。嗯.. 听起来像是 std::call_once 的工作,以确保不会发生这种情况,嗯,等一下。 :)
  • once_flag 构造函数一点也不简单:它设置内部状态以指示尚未调用任何函数。
  • @AntonSavin - 它必须是constexpr,这意味着它必须REALLY简单,如此简单以至于从这个角度来看,这种类型几乎可以被视为一个POD .
  • 确实,constexpr once_flag() noexcept; 签名在符合标准的工具链上没有解释的余地​​。
猜你喜欢
  • 2011-05-09
  • 2020-06-01
  • 1970-01-01
  • 1970-01-01
  • 2012-11-01
  • 1970-01-01
  • 2018-12-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多