如何确保 std::call_once 真的只被调用一次
Posted
技术标签:
【中文标题】如何确保 std::call_once 真的只被调用一次【英文标题】:How to ensure std::call_once really is only called 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++ 样式,请避免将函数名大写。
【参考方案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;
签名在符合标准的工具链上没有解释的余地。以上是关于如何确保 std::call_once 真的只被调用一次的主要内容,如果未能解决你的问题,请参考以下文章
使用 std::call_once 创建 singleTon 类