Indirect & Direct initialization of std::atomic in C++11/17. What are the differences?
Asked Answered
U

1

7

On this CPP Con 2017 webinar, Fedor Pikus says: "it has to be direct initialization"

enter image description here This is the link to the webinar.

What are the differences between these initialization methods? (and subsequently, why it has to be a "direct" initialization? why "indirect" initialization is "NOT"?)

// C++17 Compiler

#include <atomic>

class Example
{
    std::atomic<bool> b1_ = false; // 1-h
    std::atomic<bool> b2_{ false }; // 2-h

    static void doSomethng()
    {
        std::atomic<bool> b1 = false; // 1-f
        std::atomic<bool> b2{ false }; // 2-f
        std::atomic<bool> b3(false); // 3-f

        // Do something
    }
};
Unknot answered 10/4, 2022 at 14:37 Comment(0)
C
8

std::atomic is not copyable or movable.

Before C++17, the copy-initialization std::atomic<int> x = 0; would first construct a temporary std::atomic<int> from 0 and then direct-initialize x from that temporary. Without a move or copy constructor this would fail and so the line doesn't compile.

std::atomic<int> x(0); however is direct-initialization and will just construct x with the argument 0 to the constructor.

Since C++17 there is no temporary and x will directly be initialized by a constructor call with 0 as argument in any case and so there is no issue with std::atomic being non-movable. In that sense the slide is now out-dated.

Even though the behavior is now the same for copy-initialization and direct-initialization in this case, there are still differences between the two in general. In particular direct-initialization chooses a constructor to initialize the variable directly by overload resolution while copy-initialization tries to find an implicit conversion sequence (possibly via converting constructor or conversion function with different overload resolution rules). Also, copy-initialization, in contrast to direct-initialization, does not consider constructors marked explicit.

Regarding the code snippet in the question. 1-h and 1-f are copy-initialization as above. 3-f is direct-initialization as above. 2-h and 2-f are direct-list-initialization, which behaves different from both others in some cases, but here it has the same effect as direct-initialization with parentheses.

Explaining all the differences between the initialization forms in general would take a while. This is famously one of the most complex parts of C++.

Cryometer answered 10/4, 2022 at 14:46 Comment(6)
I always thought that the expression T x = 0; would always try to call a constructor T(int) regardless of the classPendragon
@Unknot No, the first method is still copy-initialization and the second one direct-initialization. Just the meaning of what the copy-initialization does has changed. The two methods are still different for a few reasons.Cryometer
@Pendragon See timsong-cpp.github.io/cppwp/n3337/dcl.init#16.6.2, but the temporary can be elided. It doesn't consider constructors at all, instead it will try user-defined conversion sequences.Cryometer
@Amit: His answer does explain the difference. In pre-C++17, a temporary would be created that would be copied/moved from. In post-C++17, this doesn't happen anymore. It's the behavior of the expression that has changed, not whether that expression happens to be direct or copy initialization. That is, the thing called "copy initialization" no longer requires a "copy" under certain conditions.Vancouver
@Unknot I think I mentioned the differences that are relevant here. Listing all of the differences between the two initialization forms will probably take a while. I guess the most important one is that copy-initialization doesn't consider constructors which are marked explicit.Cryometer
@Unknot Maybe #1051879 has some more information on the general case.Cryometer

© 2022 - 2024 — McMap. All rights reserved.