重载决议:分配空括号

时间:2015-11-04 00:23:08

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

我写了一些代码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
  3. 我的意图是s = S{};。如果有效的话,写s = {};会很方便。我目前正在使用s = decltype(s){};但是我宁愿避免重复类型或变量名称。

2 个答案:

答案 0 :(得分:11)

  

为什么在这里选择operator=(int),而不是“模棱两可”或另一个?

{}int是身份转换([over.ics.list]/9)。 {}S是用户定义的转化([over.ics.list]/6)(从技术上讲,它是{}const S&,并通过[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<class T> T default_constructed_instance_of(const T&) { return {}; }然后s = default_constructed_instance_of(s);会计算吗?

答案 1 :(得分:-2)

首先,案件与&#34; int&#34;无关。赋值运算符的版本,你可以删除它。您实际上也可以删除其他赋值运算符,因为它将由编译器生成。 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