我写了一些代码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
我的问题是:
operator=(int)
,而不是“模棱两可”或另一个?S
? 我的意图是s = S{};
。如果有效的话,写s = {};
会很方便。我目前正在使用s = decltype(s){};
但是我宁愿避免重复类型或变量名称。
答案 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