【问题标题】:Overload resolution: assignment of empty braces重载解决:空括号的赋值
【发布时间】:2015-11-04 00:23:07
【问题描述】:

我写了一些代码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),而不是“模糊”或其他?
  2. 是否有一个整洁的解决方法,无需更改S

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

【问题讨论】:

  • {}int 是身份转换([over.ics.list]/9)。 {}S 是用户定义的转换 ([over.ics.list]/6)。我发誓我昨天看到了一个非常相似的问题......
  • @T.C.如果是这样,我想我可以通过昨天阅读更多问题来节省一些开发时间!
  • 我的代码中的许多地方似乎都犯了同样的错误......例如我通常使用void clear() { *this = {}; } 来将对象恢复到其值初始化状态
  • 顺便说一句,optional 做了一些有趣的事情来使= {} 在所有情况下都能正常工作。

标签: c++ c++11 overload-resolution


【解决方案1】:

为什么这里选择operator=(int),而不是“模糊”或其他?

{}int 是身份转换([over.ics.list]/9)。 {}S 是用户定义的转换 ([over.ics.list]/6)(技术上是 {}const S&amp;,并经过 [over.ics.list]/8 和 [over.ics.ref]先回到 [over.ics.list]/6)。

第一个获胜。

有整洁的解决方法吗?

技巧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 = {} 使用此重载 - 因为 {} 是非推导上下文。

...不改变S?

template&lt;class T&gt; T default_constructed_instance_of(const T&amp;) { return {}; }s = default_constructed_instance_of(s); 算不算?

【讨论】:

  • 嘿,关于我现在使用template&lt;typename T&gt; void reset(T &amp;t) { t = T{}; }的最后一行
【解决方案2】:

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

第一种情况

使用复制初始化

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

这是一个构建后的复制分配,但编译器会优化这种简单的情况。你应该使用方向初始化来代替:

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

【讨论】:

  • 我仍然不明白他的第二个案例在做什么(如书面)
  • 如果我删除了赋值运算符的“int”版本,那么输出是5 5
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-07-22
  • 1970-01-01
  • 2016-10-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多