initializer_list and default constructor overload

2020-07-18 04:46发布

#include <initializer_list>
#include <iostream>
using namespace std;

struct Y {};

struct X
{
    X(initializer_list<Y>) { cout << "yay" << endl; }
    explicit X() { cout << "boo" << endl; }
};

X f()
{
    return {};
}

int main()
{
    f();

    return 0;
}

This prints out "boo". Why doesn't it print out "yay" ?

Is there anyway to differentiate the following two constructions:

  1. X()
  2. X{}

or

  1. return X();
  2. return {};

or

void g(const X&)
  1. g(X())
  2. g({})

Thanks.

标签: c++ c++11
4条回答
▲ chillily
2楼-- · 2020-07-18 04:57

It uses the default constructor because list initialization with {} is meant to be a short form of value initialization always, disregarding of other constructors, even if they are initializer list constructors.

Is there anyway to differentiate the following two constructions: ...

The X() is always value initialization, while X{} is only value initialization if X has a default constructor. If it is an aggregate, then X{} is aggregate initialization (initializing the members of X by a {} recursively). If it only has initializer list constructors and no default constructors, then X() is invalid and X{} could be valid

struct A { A(initializer_list<int>); };
A a = A{}; // valid
A b = A(); // invalid

Essentially, what X{} does depends on what X is. But X() always value initializes.

... or return X(); vs return {};

Something subtle to mention... In return {} the target is copy-list-initialized, while in return X(); first direct-initializes an X. But even though it is copy-list-initialized, it can use an explicit default constructor, because value initialization doesn't care about explicit. However when you do return {} and you try to use an explicit non-default constructor you will error out

struct A {
  explicit A(initializer_list<int>);
};

A f() { return {}; } // error!

struct B { 
  explicit B();
};

B g() { return {}; } // OK
查看更多
老娘就宠你
3楼-- · 2020-07-18 05:01

You could be a bit more explicit:

return initializer_list<Y>();
查看更多
何必那么认真
4楼-- · 2020-07-18 05:09

return {}; will always use a default constructor if there is one.

return X({}); or return {{}}; will construct from an empty initialiser list.

查看更多
三岁会撩人
5楼-- · 2020-07-18 05:17

Is there anyway to differentiate the following two constructions:

No. They are not different constructs.

The primary purpose of the {} constructor syntax was to introduce uniform initialization, to have initialization work the same everywhere. If there was a difference between them, it wouldn't be uniform.

If you want to use the empty initializer list constructor, you have to state that you're passing an initializer list explicitly. Like this: return initializer_list<Y>{}; Of course, one of the other purposes of uniform initialization is to not have to type out typenames so much, so you can achieve the same effect with return {{}};

查看更多
登录 后发表回答