【发布时间】:2015-02-26 17:10:37
【问题描述】:
为什么下面的代码无法编译,当我在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++ 中存在一个错误,当我从 A(unsigned long) 中删除 explicit 时,无论出于何种意图,编译器都会选择 B(E) 来引发歧义错误。谁能确认 clang 行为符合标准,而 VC++ 有问题?
我加了
void G(E) {};
void G(short) {};
然后像这样调用 G:
G(e1_0);
不会引发任何错误。
为什么这里G(E)是首选,而对于候选人A<short>::A(short)和B::B(E),他们是模棱两可的?
结束编辑
谢谢 --joja
【问题讨论】:
-
如果我删除
explicit,它也不会编译,它也不应该编译。模棱两可的电话不是A::A,而是F。请注意,A::A(T)不是explicit,因此对F的调用是模棱两可的。如果您将explicit添加到A::A(T),它将排除转换并且代码将编译。 -
构造函数前面的显式关键字可防止该类型的对象仅使用可充当构造函数参数的参数隐式实例化。当您从 A 的构造函数中删除 'explicit' 时,您可以简单地通过传递 'void F(A
)' 一个 short 来构造 A,这就是 F(e1_0) 似乎正在做的事情 -
@5gon12eder,您使用的是哪个编译器?在 VS2013 中,它在没有
explicit的情况下编译。我知道,模棱两可的调用是对F,但原因是将e1_0转换为F的重载接受的类型。当explicit被删除时,编译器选择B(E)进行转换并调用F(B)。我添加了explicit以确保我没有隐式使用构造函数,这是我真实代码中的一个特殊情况的构造函数。在我看来,使用explicit应该更能引导编译器使用B(E) 转换,而不是考虑将enum E升级为unsigned long。 -
不想没有
<short>(short)A
标签: c++ visual-c++ constructor overload-resolution