重载解析:空括号的赋值

Overload resolution: assignment of empty braces

我写了一些代码 S s; ... s = {};,希望它最终和 S s = {}; 一样。然而它没有。以下示例重现了该问题:

#include <iostream>

struct S
{
    S(): a(5) { }
    S(int t): a(t) {}

    S &operator=(int t)  { a = t; return *this; }
    S &operator=(S const &t) = default;

    int a;
};

int main()
{
    S s = {};

    S t;
    t = {};

    std::cout << s.a << '\n';
    std::cout << t.a << '\n';
}

输出为:

5
0

我的问题是:

  1. 为什么这里选择了operator=(int),而不是"ambiguous"或者其他的?
  2. 有没有不改变 S 的简洁解决方法?

我的意图是 s = S{};。如果可行,写 s = {}; 会很方便。我目前正在使用 s = decltype(s){};,但我希望避免重复类型或变量名称。

首先,大小写与"int"版本的赋值运算符无关,删除即可。您实际上也可以删除其他赋值运算符,因为它将由编译器生成。 IE 这种类型自动接收 copy/move 构造函数和赋值运算符。 (即它们不被禁止,您只是重复编译器使用显式符号自动执行的操作)

第一种情况

使用复制初始化:

S s = {};     // the default constructor is invoked

这是一个 post 构造复制赋值,但编译器会优化这种简单的情况。你应该使用方向初始化代替:

S s{};        // the default constructor is invoked (as you have it)

注意,你也可以这样写:

S s;          // the default constructor is invoked if you have it

第二种情况

你应该写的是复制赋值右边的直接初始化

t = S{};

此表示法将调用默认构造函数(如果有)或成员的值初始化(只要类型是聚合)。这是相关信息:http://en.cppreference.com/w/cpp/language/value_initialization

Why is operator=(int) selected here, instead of "ambiguous" or the other one?

{}int是身份转换([over.ics.list]/9). {} to S is a user-defined conversion ([over.ics.list]/6)(从技术上讲,它是{}const S&,并经过[over.ics.list]/8 和 [over.ics.ref],然后再回到 [over.ics.list]/6).

先赢。

Is there a tidy workaround?

std::experimental::optional 技巧的变体使 t = {} 总是使 t 为空。 关键是让operator=(int)成为模板。如果你想接受int并且只接受int,那么就变成了

template<class Int, std::enable_if_t<std::is_same<Int, int>{}, int> = 0>
S& operator=(Int t) { a = t; return *this; }

如果你想启用转换,可以使用不同的约束(在这种情况下你可能还想通过引用获取参数)。

要点是,通过将右操作数的类型设为模板参数,您可以阻止 t = {} 使用此重载 - 因为 {} 是非推导上下文。

...without changing S?

template<class T> T default_constructed_instance_of(const T&) { return {}; }然后s = default_constructed_instance_of(s);算吗?