【发布时间】:2019-03-11 10:27:24
【问题描述】:
考虑以下代码:
#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;
};
无论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) {}
^
据我所知,这里使用的统一初始化在两种情况下都应该选择std::vector(std::vector&&)构造函数;然而,显然g++ 将{arg} 视为初始化列表,并尝试使用应用于向量的Var 来初始化v 的第一个元素,但这是行不通的。
如果没有定义BROKEN,g++ 显然足够聪明,可以意识到 initializer_list 重载不起作用。
哪个是正确的行为,还是标准允许的?
BROKEN defined gcc
BROKEN not defined gcc
BROKEN defined clang
【问题讨论】:
-
我不是专家,但这里有一个有趣的数据点:如果您标记 ctor
explicit,那么 g++ 会更喜欢它。 -
另一个数据点:godbolt.org/z/gsc48J(您可以使用不同的优化级别)。显然,在调用
std::vector的复制构造函数之后,gcc 调用了Var::Var<std::vector<Var> >(std::vector<Var>)。 -
同理this post。
-
@xskxzr 如果我理解正确,这意味着 clang 认为初始化列表(
: value(x))中的类型错误意味着构造函数不可行,而 gcc 已经确定构造函数是在查看初始化列表之前可行。鉴于初始化列表通常可能位于不同的编译单元中,gcc 的行为似乎是合适的,这使得 clang 的不正确。这是真的吗? -
@tomsmeding:Clang 不考虑
template <typename T> Var(T x),即使你删除了函数体。 @xskxzr 链接的帖子在构造函数或初始化列表中不涉及任何可能的类型错误。
标签: c++ stdvector initializer-list uniform-initialization