由于转换错误而导致的过载/专业不正确
Incorrect overload/specialization due to wrong conversion
在我自己的代码中一个错误之后,(在我看来)编译器选择了错误的过载,我一直在挖掘解释,但找不到简单的说明。我确实找到了Herb Sutter的Gotw 49,它处理了专业问题。我还在Stackoverflow上找到了一些问题,但没有人能真正向我解释原因,也没有为我提供良好的解决方案。
我有一个单一的foo,可以从布尔来构建。我发现(很难的)std :: string也可以从bool(false)构造。
我有三种具有不同参数的方法,如下所示。一种方法接受"任何"模板参数和两个专业,接受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的两个构造师接受bool
,"无效" Foo的选择。我仅使用一个转换构造函数测试了类似版本。这些也不起作用。
问题:
- 为什么编译器尝试调用字符串题词版本?
- 为什么bar()呼叫Matter中的
<Foo>
的添加剂。 - 我如何防止这种情况发生。例如。当输入Bool时,我可以强迫编译器选择
bar( const Foo& )
吗? - 另外,有人打电话给
bar< Foo >( false )
时我可以执行编译吗?
这是四个问题的答案:
- 为什么编译器尝试调用字符串题词版本?您的
Foo
类有两个构造函数,采用bool
,这是模棱两可的,因此不考虑将bool
转换为Foo
(如果要检测到是否传递了临时或LVALUE,则可以使用bool&&
和bool const&
在C 中超载类型)。std::string
可以从char const*
构造,并且false
可以升级为null指针常数。无效的指针常数在语法上是有效的char const*
,但它是非法值,并传递会导致不确定的行为。 - 为什么bar()呼叫中的添加剂很重要?指定模板参数时,您会抑制模板参数扣除,并告诉编译器要使用哪个参数。明确指定模板参数是可以选择
bar()
的过载std::string
的唯一方法,因为该模板无法推导T
。 - 我如何防止这种情况发生。例如。当Bool输入时,我可以强迫编译器选择栏(const foo&amp;)吗?最简单的方法是让编译器推论模板参数:
bar()
S的版本从未由编译器自动选择,因为编译器无法推断模板参数。另外,如果要明确指定模板参数,并且只有bool
是一个问题,则可以添加一个Overload服用bool
并制作推论版本,并将bool
版本委托给相同的内部功能。 - 另外,当有人打电话给bar&let时,我可以执行编译错误;foo>(false)?通过删除过载,使用C 2011这很容易(请参阅下文)。
使用bool
与明确指定的模板参数创建编译时错误,您可以添加此过载:
template <typename T> void bar(bool) = delete;
删除功能可在C 2011中获得。
主要问题似乎是:如果Foo
和std::string
都可以从bool
转换,为什么会选择std::string
转换,如果调用bar<Foo>(bool)
并且可用以下过载?
template <typename T> void bar(T const&);
template <> void bar<Foo>(Foo const&);
template <typename T> void bar(std::string const&);
首先,超载分辨率选择主模板,忽略任何专业)。由于bar(std::string const&)
是比推论模板参数的版本更专业的接口,因此选择了此版本。在此阶段,第一个模板的专业化被忽略了。为了使用Foo
也适用,您可以添加
template <typename T> void bar(Foo const&);
和调用bar<Foo>(false)
将是std::string
和Foo
版本之间的歧义。
第一个示例, bar( false );
呼叫 template<typename T> void bar( const T& value )
,因为那是一个精确的与t = bool匹配。
当您指定t = foo时,两个过载都不是确切的匹配,因此您可以涉及相当复杂的规则,以了解应用哪些隐式转换。大多数C 程序员都不完全了解这些,因此最好避免避免隐性转换。
在这种情况下,最简单的修复方法就是为布尔添加另一个过载。
template<typename T>
void bar( bool )
{
std::cerr << "bool" << std::endl;
}
然后,在该超载中,您可以明确应用转换并调用所需的版本。
此行
bar< Foo >( false );
可以匹配这两个功能中的任何一个:
void bar<Foo>(const Foo&);
void bar<Foo>(const std::string&);
现在,如果两个UDT都有bool
的隐式转换构造函数,那么它应该选择哪一个?它们都有相同的优先级,因为它们均相同类型的转换。
至少...大概这就是正在发生的事情,尽管它确实不应该选择促销 构造而不是仅构造序列。
- atoi() 在应用于大型命令行参数时会产生不正确的值
- 为什么要 boost::p roperty_tree::write_json() 将整数值转换为字符串?这是不正确的
- AIX编译器13.1.3对c++进行了不正确的双重转换
- 对从引用到 std::reference_wrapper 或编译器错误的隐式转换的理解不正确?
- 输出使用 cout 转换为 ASCII 的文本时输出格式不正确,C++
- 将函数的地址转换为UINTPTR_T给我不正确的结果
- 使用 iconv 进行 UTF8 转换的输出不正确
- 将uint8_t转换为已签名的 int 是否至少有时不正确?
- 更改CV_32FC1以CV_64FC1导致不正确的数据转换
- time_t提高日期转换,给出不正确的结果
- 我创建了一个将雷亚尔转换为美元的程序,但浮点变量不正确
- 为什么隐式类型转换在此代码中结果不正确
- 在全局命名空间中重载不依赖于用户定义类型的标准定义类型的运算符是否格式正确?
- 为什么函数不能正确强制转换指针(从基类到派生类)
- 由于转换错误而导致的过载/专业不正确
- Base64 转换器的输出不正确
- 静态强制转换为不正确的类型
- 在C++中,将double转换为long的类型返回了不正确的值
- 中缀到后缀转换器 + 评估程序正确打印到控制台,但不正确地打印到文本文件
- 编译器如何优化不正确C++分层向下转换以导致真正的未定义行为