gpt4 book ai didi

c++ - 使用花括号初始化列表调用显式构造函数 : ambiguous or not?

转载 作者:可可西里 更新时间:2023-11-01 17:59:55 33 4
gpt4 key购买 nike

考虑以下几点:

struct A {
A(int, int) { }
};

struct B {
B(A ) { } // (1)
explicit B(int, int ) { } // (2)
};

int main() {
B paren({1, 2}); // (3)
B brace{1, 2}; // (4)
}

(4)brace 的构造清楚明确地调用了(2)。在 clang 上,(3)paren 的构造明确调用了 (1),而在 gcc 5.2 上,它无法编译:

main.cpp: In function 'int main()':
main.cpp:11:19: error: call of overloaded 'B(<brace-enclosed initializer list>)' is ambiguous
B paren({1, 2});
^
main.cpp:6:5: note: candidate: B::B(A)
B(A ) { }
^
main.cpp:5:8: note: candidate: constexpr B::B(const B&)
struct B {
^
main.cpp:5:8: note: candidate: constexpr B::B(B&&)

哪个编译器是正确的?我怀疑 clang 在这里是正确的,因为 gcc 中的歧义只能通过涉及隐式构造 B{1,2} 并将其传递给复制/移动构造函数的路径出现 - 但该构造函数被标记显式,因此不应允许这种隐式构造。

最佳答案

据我所知,这是一个 clang 错误

Copy-list-initialization 有一个相当不直观的行为:它认为显式构造函数在重载决议完全完成之前是可行的,但如果选择了显式构造函数,则可以拒绝重载结果。 N4567 后草案中的措辞,[over.match.list]p1

In copy-list-initialization, if an explicit constructor is chosen, the initialization is ill-formed. [ Note: This differs from other situations (13.3.1.3, 13.3.1.4), where only converting constructors are considered for copy-initialization. This restriction only applies if this initialization is part of the final result of overload resolution. — end note ]


clang HEAD 接受以下程序:

#include <iostream>
using namespace std;

struct String1 {
explicit String1(const char*) { cout << "String1\n"; }
};
struct String2 {
String2(const char*) { cout << "String2\n"; }
};

void f1(String1) { cout << "f1(String1)\n"; }
void f2(String2) { cout << "f2(String2)\n"; }
void f(String1) { cout << "f(String1)\n"; }
void f(String2) { cout << "f(String2)\n"; }

int main()
{
//f1( {"asdf"} );
f2( {"asdf"} );
f( {"asdf"} );
}

也就是说,除了注释掉对 f1 的调用外,直接来自 Bjarne Stroustrup 的 N2532 - Uniform initialization 第 4 章。感谢 Johannes Schaub 向我展示了这篇关于 std-discussion 的论文。

同一章节包含以下解释:

The real advantage of explicit is that it renders f1("asdf") an error. A problem is that overload resolution “prefers” non-explicit constructors, so that f("asdf") calls f(String2). I consider the resolution of f("asdf") less than ideal because the writer of String2 probably didn’t mean to resolve ambiguities in favor of String2 (at least not in every case where explicit and non-explicit constructors occur like this) and the writer of String1 certainly didn’t. The rule favors “sloppy programmers” who don’t use explicit.


据我所知,N2640 - Initializer Lists — Alternative Mechanism and Rationale 是最后一篇包含这种重载决议原理的论文;它的继任者 N2672 被选入 C++11 草案。

摘自“显式的含义”一章:

A first approach to make the example ill-formed is to require that all constructors (explicit and non-explicit) are considered for implicit conversions, but if an explicit constructor ends up being selected, that program is ill-formed. This rule may introduce its own surprises; for example:

struct Matrix {
explicit Matrix(int n, int n);
};
Matrix transpose(Matrix);

struct Pixel {
Pixel(int row, int col);
};
Pixel transpose(Pixel);

Pixel p = transpose({x, y}); // Error.

A second approach is to ignore the explicit constructors when looking for the viability of an implicit conversion, but to include them when actually selecting the converting constructor: If an explicit constructor ends up being selected, the program is ill-formed. This alternative approach allows the last (Pixel-vs-Matrix) example to work as expected (transpose(Pixel) is selected), while making the original example ("X x4 = { 10 };") ill-formed.

虽然这篇论文建议使用第二种方法,但它的措辞似乎有缺陷——在我对措辞的解释中,它不会产生论文的基本原理部分概述的行为。 N2672 中的措辞被修改为使用第一种方法,但我找不到任何关于为什么改变的讨论。


当然,在 OP 中初始化变量涉及的措辞略多,但考虑到 clang 和 gcc 之间的行为差​​异对于我的回答中的第一个示例程序是相同的,我认为这涵盖了要点。

关于c++ - 使用花括号初始化列表调用显式构造函数 : ambiguous or not?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49264846/

33 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com