initializer_list和默认构造函数重载决议

4
#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;
}

这会打印出 "boo"。为什么它不会打印出 "yay"?

有没有办法区分以下两个构造:

  1. X()
  2. X{}

或者

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

Thanks.


3
对于默认构造函数,使用 explicit 是没有意义的,它只用于可能被调用一次参数的构造函数,以禁止隐式类型转换。例如,只有当 MyObject 有一个非explicit 构造函数 MyObject(int) 时,才可以执行 MyObject x = 9; 这个操作。在你的示例中,编译器会忽略 explicit - Ferdinand Beyer
是的,我认为(错误地)这可能会帮助使这种过载按照我想要的方式进行。 - Andrew Tomazos
4个回答

5
有没有办法区分以下两种构造方式:
不行。它们不是不同的构造方法。
{}构造函数语法的主要目的是引入统一初始化,使初始化在任何地方都能起作用。如果它们之间有差异,那就不会是统一的了。
如果您想使用空初始化列表构造函数,则必须明确声明您正在传递初始化列表。像这样:return initializer_list<Y>{}; 当然,统一初始化的另一个目的是不必经常输入类型名称,因此您可以使用return {{}};来达到相同的效果。

你的直接回答在技术上是正确的(即不)。但请注意Mike Seymour的回答“{{}}”和“X({})”。 - Andrew Tomazos

5

return {};总是使用默认构造函数(如果有)。

return X({});return {{}};将从空初始化列表构造。


1

它使用默认构造函数,因为使用{}进行列表初始化始终意味着值初始化的简写形式,而不考虑其他构造函数,即使它们是初始化器列表构造函数。

有没有办法区分以下两种结构:...

X()始终是值初始化,而X{}仅在X具有默认构造函数时才是值初始化。如果它是一个聚合体,则X{}是聚合初始化(通过{}递归地初始化X的成员)。如果它只有初始化器列表构造函数而没有默认构造函数,则X()无效,而X{}可能有效。

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

基本上,X{} 的作用取决于 X 是什么。但是 X() 总是值初始化。

... 或者返回 X(); vs 返回 {};

需要注意的一些微妙之处... 在 return {} 中,目标是复制列表初始化,而在 return X(); 中,首先直接初始化一个 X。但即使它是复制列表初始化,它也可以使用显式默认构造函数,因为值初始化不关心 explicit。然而,当你执行 return {} 并尝试使用显式非默认构造函数时,你会出现错误。

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

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

struct B { 
  explicit B();
};

B g() { return {}; } // OK

0

你可以再明确一些:

return initializer_list<Y>();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接