当前位置:  开发笔记 > 编程语言 > 正文

使用braced-init列表调用显式构造函数:是否含糊不清?

如何解决《使用braced-init列表调用显式构造函数:是否含糊不清?》经验,为你挑选了1个好方法。

考虑以下:

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)
}

建设brace(4)清楚明确地调用(2).在铛,施工paren(3)明确要求(1),其中作为GCC 5.2,它失败编译:

main.cpp: In function 'int main()':
main.cpp:11:19: error: call of overloaded 'B()' 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}并将其传递给复制/移动构造函数的路径产生- 但是该构造函数已被标记explicit,因此不应允许这种隐式构造.



1> dyp..:

据我所知,这是一个铿锵的错误.

复制列表初始化具有相当不直观的行为:它将显式构造函数视为可行,直到重载完成完成,但是如果选择了显式构造函数,则可以拒绝重载结果.N4567后草案中的措辞,[over.match.list] p1

在copy-list-initialization中,如果explicit选择了构造函数,则初始化是错误的.[ 注意:这与其他情况(13.3.1.3,13.3.1.4)不同,其中只考虑转换构造函数进行复制初始化.此限制仅适用于此初始化是重载解析的最终结果的一部分.- 结束说明 ]


clang HEAD接受以下程序:

#include 
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初始化,第4章之外,这个问题除外.感谢Johannes Schaub向我展示了关于std讨论的这篇论文.

同章包含以下说明:

真正的优点explicit是它会f1("asdf")产生错误.一个问题是重载决策"更喜欢"非explicit 构造函数,以便f("asdf")调用f(String2).我认为解决方案f("asdf")不够理想,因为作者 String2可能并不意味着解决歧义 String2(至少不是在每种情况下,显式和非显式构造函数都是这样发生的)而作者String1肯定没有.该规则倾向于不使用的"邋program的程序员" explicit.


据我所知,N2640 - 初始化列表 - 替代机制和基本原理是最后一篇包含这种超载解决方案的理由; 它的继任者N2672被投票到C++ 11选秀中.

从其"明确的意义"一章:

使示例格式错误的第一种方法是要求所有构造函数(显式和非显式)都被考虑用于隐式转换,但如果显式构造函数最终被选中,则该程序是不正确的.这条规则可能会引入自己的惊喜; 例如:

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.

第二种方法是在查找隐式转换的可行性时忽略显式构造函数,但在实际选择转换构造函数时包含它们:如果显式构造函数最终被选中,则程序格式错误.这种替代方法允许最后一个(Pixel-vs-Matrix)示例按预期工作(transpose(Pixel)被选中),同时使原始示例(" X x4 = { 10 };")格式不正确.

虽然本文提议使用第二种方法,但其措辞似乎存在缺陷 - 在我对措辞的解释中,它不会产生本文理论部分所概述的行为.在N2672中对措辞进行了修订,以使用第一种方法,但我找不到任何有关为何更改的讨论.


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

推荐阅读
ERIK又
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有