c++ - C++ std::vector initializer_list 重载歧义 (g++/clang++)
问题描述
考虑以下代码:
#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) 都会编译它而不会出错,但如果定义了 g++ (8.2.1),则会引发错误BROKEN
:
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}
视为初始化列表并尝试初始化v
withVar
应用于向量的第一个元素,这将不起作用。
如果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;
};
现场演示。
或者:
class Class {
public:
Class(std::vector<Var> arg) : v(arg) {}
std::vector<Var> v;
};
现场演示。
现在显然在 gcc 的情况下尝试使用模板参数作为初始化列表。当使用大括号并定义了初始化列表时,初始化列表的优先级高于复制构造函数。
所以添加正确的enable_if
解决了这个问题,因为它可以防止创建构造函数的初始化列表版本。在 C++20 中,概念将以更好的方式解决这个问题。
并且当使用括号代替大括号来初始化v
初始化列表时是不可取的。
我不确定谁是对的(IMO clang,但这只是一种直觉)。也许更了解标准的人可以说出来。
推荐阅读
- postgresql - 使用 Postgres 的 Superset 行级安全性未正确读取表达式 {{current_username()}}
- c++ - 为什么即使我只使用 const int,它也会说“数字常量之前的预期不合格 ID”?
- java - 从 POSTMAN 收到的数据与 JAVA rest api 调用相比不匹配
- elasticsearch - 什么是 ElasticSearch 中的最大大小索引
- azure - Azure YAML 部署非常慢
- spring - XPath 语句没有生成节点来验证 Spring Boot SOAP 请求
- android - 使用泛型实现 Kotlin 动态数组
- blueprism - 如何在 BluePrism Calculation 中替换字符串
- c++ - 可视化向量范数的问题(OpenGL C++)
- c# - Jira .NET SDK:issue.Savechange() 抛出未知异常