是什么原因导致这个构造函数在获取初始化列表并委托向量时委托给自己?

LB-*_*B-- 7 c++ c++11 c++14

#include <initializer_list>
#include <vector>

struct test
{
    using t = std::vector<test>;
    test(t const &v)
    {
    }
    test(t &&v)
    {
    }
    test(std::initializer_list<test> v)
    : test{t{v}} //error
    {
    }
};
Run Code Online (Sandbox Code Playgroud)

无论GCC抱怨说,第三个构造函数,一个以初始化列表中,代表们本身.我不明白这是怎么可能的,因为你无法从向量构造初始化列表.

通过用圆括号替换外部花括号来修复错误是微不足道的,但为什么这首先是一个问题呢?这几乎相同的程序编译得很好:

#include <initializer_list>

struct a {};
struct b {};

struct test
{
    test(a const &)
    {
    }
    test(a &&)
    {
    }
    test(std::initializer_list<b> v)
    : test{a{}} //no error, still using curly braces
    {
    }
};
Run Code Online (Sandbox Code Playgroud)

有趣的是,使用上面的第二个例子,如果你用替换b,错误会重新出现test.有人能解释一下这里发生了什么吗?

vso*_*tco 7

类型t{v}std::vector<test>.我们的想法是init-list构造函数总是优先于任何其他构造函数,因此test{t{v}}首先尝试调用init-list构造函数(如果存在),以及类型是否兼容.在你的情况下,这是可能的,因为test它本身可以从std::vector<test>(通过你的前2个构造函数)隐式构造,因此编译器最终委托给init-list构造函数,因此错误.

在第二种情况下,没有歧义,因为类型a{}不再可以隐式转换std::initializer_list<b>.

explicit在第一个示例中创建构造函数,或者调用基础构造函数test(t{v}),并且您的歧义将消失(编译器将不再执行隐式转换).

一个更简单的例子(在这里生活)表现出与第一个例子基本相同的行为:

#include <initializer_list>

struct test
{
    /*explicit*/ test(int){} // uncomment explicit and no more errors
    test( std::initializer_list<test> v)
        : test{42} {} // error, implicitly converts 42 to test(42) via the test(int)
};

int main(){}
Run Code Online (Sandbox Code Playgroud)

处理init-list构造函数的标准的相关部分是§13.3.1.7/ 1 [over.match.list] - 下面的引文摘自@Praetorian现在删除的答案 -

当非聚合类类型T的对象被列表初始化,使得8.5.4指定根据本节中的规则执行重载解析时,重载决策分两个阶段选择构造函数:
- 最初,候选函数是初始化器类的-list构造函数(8.5.4)T和参数列表由初始化列表作为单个参数组成.
- 如果找不到可行的初始化列表构造函数,则再次执行重载解析,其中候选函数是类的所有构造函数,T参数列表由初始化列表的元素组成.

  • 钉它(+1); 如果有办法折磨您的代码使用`initalizer_list`构造函数,编译器必须这样做.@ Praetorian现已删除的答案包含相关引文. (2认同)