在我自己的代码中出现错误之后,(在我看来)编译器选择了错误的重载,我一直在寻找解释,但找不到一个简单的解释。我确实找到了处理专业化问题的Herb Sutter 的 GOTW 49。我还在stackoverflow上发现了一些问题,但没有一个能真正向我解释原因,也不能为我提供好的解决方案。
我有一个类 Foo,它可以从布尔值构造。我发现(很难)std::string 也可以从 bool (假)构造。
我有三个具有不同参数的(模板)方法,如下所示。一个方法接受“任何”模板参数,两个特化,接受一个struct Foo,另一个接受一个字符串。
#include <string>
#include <iostream>
struct Foo
{
Foo() : value( false ){ };
Foo( bool v ) : value ( v ) { }
Foo( const bool& v ) : value( v ) { }
bool value;
};
template< typename T >
void bar( const T& value )
{
std::cerr << "template bar" << std::endl;
}
template< >
void bar< Foo >( const Foo& )
{
std::cerr << "template bar with Foo" << std::endl;
}
template< typename T >
void bar( const std::string& )
{
std::cerr << "template bar with string" << std::endl;
}
int main( int argc, char* argv[] )
{
bar( false ); // Succeeds and calls 1st bar( const T& )
bar< Foo >( false ); // Crashes, because 2nd bar( const std::string& )
// is called with false promoted to null pointer.
return 0;
}
我已经使用 Visual Studio 2010 和 MinGW (gcc 4.7.0) 对此进行了测试。GCC 很好地给出了编译警告,但 msvc 没有:
main.cpp:34:20: warning: converting 'false' to pointer type for argument 1 of 'std::basic_string< ... ' [-Wconversion-null]
小更新(在代码中):即使是 Foo 的显式特化也不起作用。\
小更新 2:编译器不会抱怨“模棱两可的重载”。
小更新 3:有人回答说 Foo 的两个构造函数接受 a bool
,“使”Foo 的选择“无效”。我只用一个转换构造函数测试了类似的版本。这些也不起作用。
问题:
- 为什么编译器会尝试调用字符串参数版本?
- 以及为什么添加
<Foo>
bar() 调用很重要。 - 我怎样才能防止这种情况发生。例如,我可以强制编译器在
bar( const Foo& )
输入布尔值时进行选择吗? - 或者,当有人调用时,我可以强制执行编译错误
bar< Foo >( false )
吗?