C std :: vector Initializer_list过载歧义(g /clang )

C++ std::vector initializer_list overload ambiguity (g++/clang++)

本文关键字:歧义 clang vector std Initializer list      更新时间:2023-10-16

考虑以下代码:

#include <vector>
#define BROKEN
class Var {
public:
#ifdef BROKEN
    template <typename T>
    Var(T x) : value(x) {}
#else
    Var(int x) : value(x) {}
#endif
    int value;
};
class Class {
public:
    Class(std::vector<Var> arg) : v{arg} {}
    std::vector<Var> v;
};

clang (7.0.1)是否定义了BROKEN,毫无疑问地对此进行了编译,但是G (8.2.1)如果定义了BROKEN,则会引起错误:

main.cpp:9:20: error: cannot convert ‘std::vector<Var>’ to ‘int’ in initialization
  Var(T x) : value(x) {}
                    ^
据我所知,此处使用的统一的Intialization在两种情况下都应选择std::vector(std::vector&&)构造函数。但是,显然,g++相反,将{arg}视为初始化列表,并尝试使用应用于向量的Var初始化v的第一个元素,该元素将其应用于向量。

如果未定义BROKEN,G 显然足够聪明,可以意识到initializer_list过载不起作用。

哪个是正确的行为,或者标准允许?

断开定义的GCC
破裂未定义的GCC
折断的clang

这是解决该问题的两种方法:

class Var {
public:
#ifdef BROKEN
    template <typename T>
    Var(T x, typename std::enable_if<std::is_convertible<T, int>::value>::type* = nullptr) : value(x) {}
#else
    Var(int x) : value(x) {}
#endif
    int value;
};

现场演示。

或:

class Class {
public:
    Class(std::vector<Var> arg) : v(arg) {}
    std::vector<Var> v;
};

现场演示。

现在显然在GCC尝试时使用模板参数作为初始化列表。当使用括号并定义初始化列表时,初始化列表具有更高的优先级,然后复制构造函数。

因此,添加适当的enable_if解决了问题,因为它可以保护创建构造函数的初始化列表版本。在C 中,20个概念将以更好的方式解决此问题。

,当使用括号用于初始化 v初始化列表时,不太可取。

我不确定谁是对的(Imo Clang,但这只是一种直觉)。也许一个知道标准更好的人可以说。