20

Simple question about C++11 syntaxis. There is a sample code (reduced one from source)

struct Wanderer
{
  explicit Wanderer(std::vector<std::function<void (float)>> & update_loop)
  {
    update_loop.emplace_back([this](float dt) { update(dt); });
  }
  void update(float dt);
};

int main()
{
  std::vector<std::function<void (float)>> update_loop;
  Wanderer wanderer{update_loop}; // why {} ???
}

I'd like to know, how it can be possible call constructor with curly brackets like Wanderer wanderer{update_loop}; It is neither initializer list, nor uniform initialization. What's the thing is this?

Federico klez Culloca
  • 26,308
  • 17
  • 56
  • 95
Loom
  • 9,768
  • 22
  • 60
  • 112
  • But it is uniform initialization. Braces can be used to call constructors, though the constructor taking a `std::initializer_list` of that type is always preferred. – chris Mar 13 '13 at 20:47
  • `// why {} ???` -- Good question. The is no reason to prefer this over `Wanderer wanderer(update_loop)`. – ipc Mar 13 '13 at 20:50
  • 2
    @ipc In some cases, it can avoid the most vexing parse. – juanchopanza Mar 13 '13 at 20:51
  • @juanchopanza: But in other cases it can silently change the meaning of the code one time. I'm not a fan of "uniform" initialization. – ipc Mar 13 '13 at 20:53
  • @ipc sure, that is a pretty ugly feature of this initialization. – juanchopanza Mar 13 '13 at 20:54

3 Answers3

27

It is neither initializer list, nor uniform initialization. What's the thing is this?

Your premise is wrong. It is uniform initialization and, in Standardese terms, direct-brace-initialization.

Unless a constructor accepting an std::initializer_list is present, using braces for constructing objects is equivalent to using parentheses.

The advantage of using braces is that the syntax is immune to the Most Vexing Parse problem:

struct Y { };

struct X
{
    X(Y) { }
};

// ...

X x1(Y()); // MVP: Declares a function called x1 which returns
           // a value of type X and accepts a function that
           // takes no argument and returns a value of type Y.

X x2{Y()}; // OK, constructs an object of type X called x2 and
           // provides a default-constructed temporary object 
           // of type Y in input to X's constructor.
Andy Prowl
  • 124,023
  • 23
  • 387
  • 451
  • "Unless a constructor accepting an std::initializer_list is present,..." And if such a constructor is present? – junvar Jul 11 '19 at 19:19
9

It is just C++11 syntax. You can initialize objects calling their constructor with curly braces. You just have to bear in mind that if the type has an initializer_list constructor, that one takes precedence.

juanchopanza
  • 223,364
  • 34
  • 402
  • 480
1

In addition, braces-constructors do not allow narrowing, similarly to braces-initialization.
Let's take a look at the simple constructor taking and printing an integer value:

class Test {
public:
        Test(int i) {
                std::cout << i << std::endl;
        }
};

While Test test(3.14); compiles and outputting narrowed 3,
Test test{3.14}; would not even compile:

error: narrowing conversion of ‘3.1400000000000001e+0’ from ‘double’ to ‘int’ [-Wnarrowing]
   11 |  Test test{3.14};
      |                ^