为什么以下代码无法编译,当我在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
答案 0 :(得分:3)
让我们一个接一个地看看你的例子的各种变化。
调用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)
在三种可能性中
e0
转换为unsigned long
,通过构造函数(2)从中创建A<short>
并调用重载(6),e0
转换为short
,通过构造函数(3)从中创建A<hort>
并调用overload(6)和B
创建e0
并调用overload(7)第一个选项不适用,因为(2)是explicit
。其余两个都涉及用户定义的转换,这些转换被认为同样好,没有一个转向另一个。电话不明确,程序格式不正确。
让我们从构造函数中删除explicit
并调用f(e0)
。
template<typename T>
struct A
{
A(); // (1)
A(unsigned long); // (2)
A(T); // (3)
};
struct B
{
B(); // (4)
B(E); // (5)
};
这三个选项保持不变,但这一次,这三个选项都适用,并且调用(甚至更多)模糊不清,程序格式不正确。
让我们构建两个构造函数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)。
让我们制作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)
};
这次,三个转换路径中的 none 是适用的,因为每个转换路径都会通过explicit
构造函数。 f
没有适用的重载且程序格式不正确。
致电g(e0)
。
我们有两种可能性:
e0
转换为short
并调用重载(9)。在这两个中,第一个选项明显有利,因为它不涉及转换。电话是明确的。 (即使构造函数(5)不是explicit
。)
请注意,默认构造函数(1)和(4)实际上对此讨论没有任何贡献。使用GCC 4.9.1进行测试,所有五个示例都按预期运行。