Based on the initialization order

According to the C ++ 14 standard, non-static member variables are initialized in the order in which they are declared in the class. The code abbreviated below relies on this rule to control the flow function.

class foo { foo(): keep_going{true}, my_thread(&foo::go,this) {} void go() { while(keep_going) check a std::condition_variable and do some work; } bool keep_going; std::thread my_thread; } 

Note that keep_going declared before the stream object and must be set to true by the time the stream enters the go function. This is normal and seems to work fine.

However, this is multi-threaded code, and it pays for paranoid, so I have two questions:

1 Can one rely on the initialization order as follows? My real object does not make sense without a workflow, so I want to set it in the constructor.

2 Is it unsafe to give code to others when it relies on relatively obscure things, such as the initialization order?

+7
c ++ c ++ 14
source share
3 answers
  • It is safe in accordance with the standard.

  • Extremely dangerous. Few people know about this, and someone supporting your header file can change the order of members with disastrous consequences.

I would not rely on it.

+5
source share

Although standard is safe, I would not go with it.

Anecdote: I wrote a custom ThreadPool on Windows using visual studio 2013. I declared the thread pool global. Of course, by standard, global objects are destroyed when main returns. the thread pool destructor tried to join each thread, but alas! dead castle. (you can read about this problem here: std :: thread :: join () hangs if called after main () exits when using VS2012 RC ). The standard clearly states that if a stream is joinable, there is no problem connecting it, but as you can see, it was not completely implemented.

Why am I telling you this unrelated problem? Because even compilers and platforms have some bugs. Subtle things cannot be implemented 100% correctly in the first few relevant versions of the compiler.

That is why I would not go with this idea. In the process, I declared a stream enclosed in std::unique_ptr and initialized it in the constructor body. Thus, there is no way to initialize it before keep_going .

 foo(): keep_going{true} { my_thread = std::make_unique<std::thread>(&foo::go,this); } 
+4
source share

I would like to rewrite the code to make the code obvious even to a monkey.

Another potential problem may arise when the class foo is a base class. The stream begins with an incompletely constructed object. What happens if the constructor of the derived class fails? In this case, it is better to execute the thread from the constructor to the start() method.

+2
source share

All Articles