C++ 初始化列表重载消歧

问题描述

我有一个关于 C++ 初始化列表消歧的问题,它在 gcc、clang 和 Visual Studio 之间表现出不同的行为。

我想知道这是“未定义的行为”(不正确的程序)还是这些编译器之一有错误。有什么想法吗?

考虑以下声明:

class Arg
{
public:
    Arg(int i);
};

class Object
{
public:
    Object(const char* str,int i);
    Object(const char* str,const std::initializer_list<Arg>& args);
};

现在这个用法:

Object c("c",{4});

应该使用哪个构造函数?带有 int 的(假设文字周围的大括号是多余的)或带有初始化列表的(从 int 隐式转换为 Arg)。

GCC 10.2.0 选择初始化列表为 Arg 的构造函数。

Clang 11.2.2-2 选择带有 int 的构造函数并报告有关大括号的警告:

initlist.cpp:46:19: warning: braces around scalar initializer [-Wbraced-scalar-init]
    Object c("c",{4});
                  ^~~

Visual Studio 2019 16.8.6 选择带有 int 的构造函数而没有警告 (/W4)。

从多数人的角度来看,具有 int 的构造函数获胜。另一方面,如果我们直接使用 std::initializer_list<int> 而不是 std::initializer_list<Arg>(没有隐式调用 Arg 构造函数),所有三个编译器都会选择带有初始化列表的构造函数。

由于歧义和行为的差异,无论如何都应该避免这种代码。但我很想知道谁错了?未定义的应用程序代码或编译器之一?

完整源代码如下,以防有人想尝试:

#include <iostream>

class Arg
{
public:
    int value;
    Arg(int i);
};

class Object
{
public:
    Object(const char* str,const std::initializer_list<Arg>& args);
};


Arg::Arg(int i) : value(i)
{
    std::cout << "Arg(" << i << ")" << std::endl;
}

Object::Object(const char* str,int i)
{
    std::cout << "Object(\"" << str << "\"," << i << ")" << std::endl;
}

Object::Object(const char* str,const std::initializer_list<Arg>& args)
{
    std::cout << "Object(\"" << str << "\",{";
    bool comma = false;
    for (auto it = args.begin(); it != args.end(); ++it) {
        if (comma) {
            std::cout << ",";
        }
        comma = true;
        std::cout << it->value;
    }
    std::cout << "})" << std::endl;
}

int main(int argc,char* argv[])
{
    Object a("a",1);
    Object b("b",{2,3});
    Object c("c",{4});
}

使用海湾合作委员会:

Object("a",1)
Arg(2)
Arg(3)
Object("b",3})
Arg(4)
Object("c",{4})

使用 clang 和 VS:

Object("a",3})
Object("c",4)

解决方法

{4}const std::initializer_list<Arg>& 是用户定义的转换。

{4}int 是标准转换。

后者获胜。这是一个 GCC 错误。

当转换序列具有相同形式时,对 initializer_list 的列表初始化优于其他。他们不在这里。

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...