tom*_*ing 10 c++ stdvector initializer-list uniform-initialization
考虑以下代码:
#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;
};
Run Code Online (Sandbox Code Playgroud)
不管是否BROKEN定义,Clang ++(7.0.1)都会编译此错误,而如果BROKEN已定义,则g ++(8.2.1)会引发错误:
main.cpp:9:20: error: cannot convert ‘std::vector<Var>’ to ‘int’ in initialization
Var(T x) : value(x) {}
^
Run Code Online (Sandbox Code Playgroud)
据我所知,std::vector(std::vector&&)在两种情况下,这里使用的统一初始化都应选择构造函数。但是,显然,g++它将{arg}视为初始化列表,并尝试初始化应用于向量的vwith 的第一个元素Var,这将不起作用。
如果BROKEN未定义,则g ++显然足够聪明,可以意识到initializer_list重载将无法正常工作。
哪个是正确的行为,或者标准都允许?
以下是解决该问题的两种方法:
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;
};
Run Code Online (Sandbox Code Playgroud)
现场演示。
或者:
class Class {
public:
Class(std::vector<Var> arg) : v(arg) {}
std::vector<Var> v;
};
Run Code Online (Sandbox Code Playgroud)
现场演示。
现在显然在 gcc 的情况下尝试使用模板参数作为初始化列表。当使用大括号并定义初始化列表时,初始化列表的优先级高于复制构造函数。
因此,添加适当的内容enable_if可以解决问题,因为它可以防止创建构造函数的初始化列表版本。在 C++20 中,概念将以更好的方式解决这个问题。
当使用括号而不是大括号来初始化v初始化列表时,并不可取。
我不确定谁是对的(IMO 叮叮当当,但这只是一种直觉)。也许更了解标准的人可以告诉。