Brace-init-list和赋值

时间:2017-04-06 02:29:11

标签: c++ c++11 implicit-conversion list-initialization

#include <iostream>

struct int_wrapper
{
    int i;

    int_wrapper(int i = 0) : i(i) {}
};

struct A
{
    int_wrapper i;
    int_wrapper j;

    A(int_wrapper i = 0, int_wrapper j = 0) : i(i), j(j) {}
};

int main()
{
    A a;

    a = {3};

    // error: no match for ‘operator=’ (operand types are ‘A’ and ‘int’)
    // a = 3;

    std::cout << a.i.i << std::endl;

    return 0;
}

我知道在进行隐式转换时不允许多个用户转换,但是,为什么使用brace-init-list双重用户转换有效?

1 个答案:

答案 0 :(得分:5)

请记住:使用braced-init-list意味着&#34;初始化一个对象&#34;。你得到的是隐式转换,然后是对象初始化。您正在获得&#34;双重用户转换&#34;因为那是你要求的

执行a = <something>;时,其效果与a.operator=(<something>)相当。

当某个东西是braced-init-list时,这意味着它会a.operator=({3})。这将基于从braced-init-list初始化它们的第一个参数来选择operator=重载。将被调用的重载将是一个可以通过braced-init-list中的值初始化的类型。

该运算符有两个重载。即,复制分配和移动分配。由于这个braced-init-list初始化一个prvalue,所以要调用的首选函数将是move-assignment操作符(不是重要的,因为它都会导致同样的事情)。移动分配的参数是A&&,因此braced-init-list将尝试初始化A。它将通过复制列表初始化的规则来实现。

选择要调用的operator=函数后,我们现在初始化A。由于A的构造函数不是explicit,因此copy-list-initialization可以自由调用它。由于该构造函数有2个默认参数,因此只能使用一个参数调用它。 A的构造函数int_wrapper中的第一个参数的类型可以从braced-init-list int中的第一个值的类型隐式转换。

因此,您获得了int_wrapper的隐式转换,复制列表初始化使用它来初始化prvalue临时对象,该对象用于通过类型A分配给现有对象移动赋值。

相比之下,a.operator=(3)会尝试直接从3隐式转换为A。这当然需要两个转换步骤,因此是不允许的。

请记住,braced-init-lists意味着&#34;初始化一个对象&#34;。