当 T 从 int 隐式转换时,为什么 std::vector<T> v{1, 2, 3} 和 std::vector<T> v = {1, 2, 3} 调用不同的构造函数?

问题描述 投票:0回答:1

问题 - 请参阅编译器资源管理器

如果我创建一个

std::vector<int>
并以两种方式初始化它,都会调用
std::initializer_list
构造函数。

std::vector<int>  v1{1, 2, 3};     // Calls vector(initializer_list< int >)
std::vector<int>  v2 = {1, 2, 3};  // Calls vector(initializer_list< int >)

但是如果我使用一个从

int
隐式转换的类:

struct Imp { Imp(int) {} };

然后第二个初始化方法调用带有一对迭代器的构造函数。

std::vector<Imp>  v3{1, 2, 3};     // Calls vector(initializer_list< Imp >)
std::vector<Imp>  v4 = {1, 2, 3};  // Calls vector(const int*, const int* ) ???

这只发生在使用GCC 14.2时。使用 Clang 19.1MSVC 19.40 时,

v3
v4
都会调用
initializer_list
构造函数。优化级别没有什么区别

为什么

= {}
语法在 GCC 上调用
vector(const int*, const int*)

进一步调查

我尝试创建自己的具有两个构造函数的类模板:

template <typename T>
struct Custom {
    Custom(std::initializer_list<T>) {}
    Custom(const int*, const int*)   {}
};

现在

{}
= {}
语法都会调用 GCC 上的
initializer_list
构造函数。

Custom<Imp>       v5{1, 2, 3};     // Calls Custom(initializer_list< Imp >)
Custom<Imp>       v6 = {1, 2, 3};  // Calls Custom(initializer_list< Imp >)


事情变得真正令人困惑的是,如果我专门针对我的隐式转换类型

std::vector
。那么分三种情况:

案例1 - 仅提供Imp ctor

initializer_list

Result - 两种语法都调用提供的 ctor template <> struct std::vector<Imp> { vector(initializer_list<Imp>) {} };


案例2 - 仅提供std::vector<Imp> v7{1, 2, 3}; // Calls vector(initializer_list< Imp >) std::vector<Imp> v8 = {1, 2, 3}; // Calls vector(initializer_list< Imp >) ctor

const int*

结果 - 两种语法都无法编译(没有匹配的构造函数) template <> struct std::vector<Imp> { vector(const int*, const int*) {} };


案例 3 - 提供两个 ctors std::vector<Imp> v9{1, 2, 3}; // Error - no matching ctor std::vector<Imp> vA = {1, 2, 3}; // Error - no matching ctor

Result - template <> struct std::vector<Imp> { vector(initializer_list<Imp>) {} vector(const int*, const int*) {} }; 语法调用

{}
构造函数,而
initializer_list
语法调用
= {}
构造函数
const int*

这就是我迷路的地方。



案例3中,提供std::vector<Imp> vB{1, 2, 3}; // Calls vector(initializer_list< Imp >) std::vector<Imp> vC = {1, 2, 3}; // Calls vector(const int*, const int*) ??? 构造函数如何允许调用

initializer_list
构造函数?
编辑

有人问我如何知道正在调用哪个构造函数。当我查看何时调用复制构造函数和移动构造函数时,就开始了这一点。

const int*

无法移动。但这里还有一些带有打印内容的其他示例。

示例 1 - 使用独立类型 std::initializer_list

打印:

struct Imp { Imp(int) {} }; template <typename T> struct Custom { Custom(std::initializer_list<T>) { std::printf("initializer_list<T>\n"); } Custom(const int*, const int*) { std::printf("const int*, const int*\n"); } }; int main(int argc, char *argv[]) { Custom<Imp> v{1, 2, 3}; Custom<Imp> w = {1, 2, 3};

示例 2 - 使用 std::vector 专门化 initializer_list<T> initializer_list<T>

打印:

struct Imp { Imp(int) {} }; template <> struct std::vector<Imp> { vector(initializer_list<Imp>) { std::printf("initializer_list<T>\n"); } vector(const int*, const int*) { std::printf("const int*, const int*\n"); } }; int main(int argc, char *argv[]) { std::vector<Imp> v{1, 2, 3}; std::vector<Imp> w = {1, 2, 3};

这两个示例之间的
唯一

区别在于,一个是 std:: 专业化,而另一个则不是。 为什么这会改变调用哪个 ctor 重载?

c++ gcc constructor overloading initializer-list
1个回答
0
投票

自 GCC 13 起,GCC 重写了某些列表初始化以使用迭代器构造函数。这是为了解决使用字符串文字列表构造

initializer_list<T> const int*, const int*

时效率低下的问题。请参阅

GCC 错误 105838
GCC 的源代码中描述了此行为。

如果我们要打电话,例如向量(initializer_list
)开始 带有字符串文字列表(效率低下,请参阅 PR105838), 相反,构建一个 const char* 数组并将其传递给范围构造函数。 但仅对标准库类型执行此操作,我们可以假设 转型是有道理的。

© www.soinside.com 2019 - 2024. All rights reserved.