gpt4 book ai didi

c++ - "explicit"构造函数对重载决议的影响

转载 作者:塔克拉玛干 更新时间:2023-11-03 00:39:02 28 4
gpt4 key购买 nike

为什么下面的代码不能编译,而当我在类 A 中删除构造函数之前的显式关键字时,它可以编译?

使用 Visual Studio 2013:

enum E { e1_0, e1_1 };

template<typename T>
struct A
{
A() {}
explicit A(unsigned long) {}
A(T) {}
};

struct B
{
B() {}
B(E) {}
};


void F(B) {};
void F(A<short>) {};

void test()
{
F(e1_0);
}

错误:

1>------ Build started: Project: exp_construct_test, Configuration: Debug Win32 ------
1> exp_construct_test.cpp
1>e:\exp_construct_test\exp_construct_test.cpp(23): error C2668: 'F' : ambiguous call to overloaded function
1> e:\exp_construct_test\exp_construct_test.cpp(19): could be 'void F(A<short>)'
1> e:\exp_construct_test\exp_construct_test.cpp(18): or 'void F(B)'
1> while trying to match the argument list '(E)'
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

编辑: 我下载了 clang 并使用 clang-cl 编译,这两种情况都报告了错误。因此,正如评论中指出的那样,歧义介于 A<short>(short) 之间。和 B(E) .

所以也许 VC++ 中有一个错误,当我删除 explicit 时来自 A(unsigned long) ,无论出于何种意图,编译器都会选择 B(E) 而不是引发歧义错误。谁能确认 clang 行为是符合标准的,而 VC++ 是错误的?

我加了

void G(E) {};
void G(short) {};

然后像这样调用 G:

G(e1_0);

这不会引发任何错误。为什么在这里G(E)优先考虑,以防考生A<short>::A(short)B::B(E) , 他们是模棱两可的?

结束编辑

谢谢--joja

最佳答案

让我们一个接一个地看一下您的示例的各种变体。

  1. 原始例子调用f(e0) .

    enum E {e0, e1};

    template<typename T>
    struct A
    {
    A(); // (1)
    explicit A(unsigned long); // (2)
    A(T); // (3)
    };

    struct B
    {
    B(); // (4)
    B(E); // (5)
    };

    void f(A<short>); // (6)
    void f(B); // (7)

    void g(E); // (8)
    void g(short); // (9)

    三种可能性之中

    • 转换e0unsigned long , 创建一个 A<short>通过构造函数 (2) 从中调用重载 (6),
    • 转换e0short , 创建一个 A<hort>从它通过构造函数 (3) 调用重载 (6) 和
    • 创建一个 B来自 e0通过构造函数 (5) 并调用重载 (7)

    第一个选项不适用,因为 (2) 是 explicit .其余两个都涉及用户定义的转换,它们被认为同样好,没有一个会偏袒另一个。调用不明确且程序格式错误。

  2. 让我们删除 explicit从构造函数中调用 f(e0) .

    template<typename T>
    struct A
    {
    A(); // (1)
    A(unsigned long); // (2)
    A(T); // (3)
    };

    struct B
    {
    B(); // (4)
    B(E); // (5)
    };

    三个选项保持不变,但这次,所有三个选项都适用,并且调用(甚至)不明确且程序格式错误。

  3. 让我们把两个构造函数都设为explicit并调用f(e0) .

    template<typename T>
    struct A
    {
    A(); // (1)
    explicit A(unsigned long); // (2)
    explicit A(T); // (3)
    };

    struct B
    {
    B(); // (4)
    B(E); // (5)
    };

    这使得隐式构造 A<short> 变得不可能并且调用明确指的是重载 (5)。

  4. 让我们制作B的构造函数 explicit也调用f(e0) .

    template<typename T>
    struct A
    {
    A(); // (1)
    explicit A(unsigned long); // (2)
    explicit A(T); // (3)
    };

    struct B
    {
    B(); // (4)
    explicit B(E); // (5)
    };

    这次,没有三种转化路径适用,因为每条转化路径都会经过 explicit构造函数。 f 没有过载适用且程序格式错误。

  5. 调用g(e0) .

    我们这里有两种可能性:

    • 调用重载(8)而不进行任何转换或
    • 转换e0short并调用重载 (9)。

    在这两个中,第一个选项显然是有利的,因为它不涉及转换。这个电话是明确的。 (即使构造函数 (5) 不是 explicit 。)

请注意,默认构造函数 (1) 和 (4) 实际上对本次讨论没有任何贡献。使用 GCC 4.9.1 进行测试,所有五个示例都按预期运行。

关于c++ - "explicit"构造函数对重载决议的影响,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28748564/

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