C++支持可变模版参数
1
| template <typename... Args> void foo(Args... args);
|
而std::call_once
准确执行一次可调用 (Callable) 对象 f ,即使同时从多个线程调用。
有时候为了方便会把std::once_flag
声明在函数内中,比如说
1 2 3 4 5
| void callOne() { static std::once_flag flag; std::call_once(flag, []() { }); }
|
但是和可变模版参数结合在一起就一些看起来神奇的特性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| static int counter{0};
template <typename... Args> void test(Args... args) { static std::once_flag flag; static int calls{0}; std::call_once(flag, [&]() { std::cout << "counter:" << ++counter << " call_once_times:" << ++calls << std::endl; }); }
int main() { test(); test(1); }
|
可以发现有两行输出
1 2
| counter:1 call_once_times:1 counter:2 call_once_times:1
|
这应该是模版展开了,其实是两个不同的函数了。解决方法也很简单,把flag
变量往上提即可。
问题的发现
写单例模式时,类似于这样
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| #include <mutex> #include <utility>
template <typename T> class Singleton { public: template <typename... Args> static T *getInstance(Args... args);
private: static inline T *instance{nullptr}; };
template <typename T> template <typename... Args> T *Singleton<T>::getInstance(Args... args) { static std::once_flag flag; std::call_once(flag, [&]() { instance = new T(std::forward<Args>(args)...); });
return instance; };
int main() { Singleton<int>::getInstance(1); Singleton<int>::getInstance(); }
|
第二次会得到一个新的实例。问题就出在static std::once_flag flag
不应该在函数体中定义,应该在类中声明定义。