简体   繁体   English

Visual Studio中涉及void *,string和const char []的意外重载解析

[英]Unexpected overload resolution in visual studio involving void*, string and const char[]

I got following unexpected overload resolution behavior with the visual studio compiler (tested in VS2010 and VS2012). 我使用visual studio编译器(在VS2010和VS2012中测试)获得了意外的重载解析行为。

Minimal example: 最小的例子:

#include <iostream>
#include <string>

void f(void *)
{
    std::cout << "f(void*)\n";
}

void f(const std::string &)
{
    std::cout << "f(const std::string &)\n";
}

int main()
{
    f("Hello World!");
}

Output: 输出:

> f(void *)

Expected Ouptut: 预期的Ouptut:

> f(const std::string &)

Compiling with GCC(tested with 4.6.3) generates the expected output. 使用GCC进行编译(使用4.6.3测试)可生成预期输出。

If I comment out the "const std::string &" version of f(), visual studio happily compiles on /W4 without any warnings, while GCC emits following error (as expected): "invalid conversion from 'const void*' to 'void*' [-fpermissive]". 如果我注释掉f()的“const std :: string&”版本,visual studio很乐意在没有任何警告的情况下编译/ W4,而GCC会发出以下错误(如预期的那样):“从'const void *'无效转换为'void *'[-fpermissive]“。

Does anyone know why visual studio behaves in that way, choosing basically a const cast overload over a conversion to std::string for char[]? 有谁知道为什么visual studio以这种方式运行,基本上选择const转换重载转换为std :: string for char []?

Is there any way to prohibit this behavior, or at least get VS to generate a warning? 有没有办法禁止这种行为,或者至少让VS生成警告?

For VS 2013 Microsoft documents silently dropping const for string literals as a Microsoft-specific behavior for C++: 对于VS 2013 Microsoft文档静默地删除字符串文字的const作为C ++的Microsoft特定行为:

Microsoft Specific 微软特定

In Visual C++ you can use a string literal to initialize a pointer to non-const char or wchar_t. 在Visual C ++中,您可以使用字符串文字初始化指向非const char或wchar_t的指针。 This is allowed in C code, but is deprecated in C++98 and removed in C++11. 这在C代码中是允许的,但在C ++ 98中已弃用,在C ++ 11中已删除。

... ...

You can cause the compiler to emit an error when a string literal is converted to a non_const character when you set the /Zc:strictStrings (Disable string literal type conversion) compiler option. 当您在设置/Zc:strictStrings (禁用字符串文字类型转换)编译器选项时将字符串文字转换为non_const字符时,可能会导致编译器发出错误。

For versions earlier than VS 2013 (for example VS 2012's documentation ), Microsoft documents string literals in C++ as using the C convention of being non-const array of char . 对于VS 2013之前的版本(例如VS 2012的文档 ),Microsoft将C ++中的字符串文字记录为使用非常量char数组的C约定。

I don't see why it is unexpected. 我不明白为什么会出乎意料。 The conversion of char const[] to a std::string involves a user defined conversion; char const[]std::string转换涉及用户定义的转换; the conversion to void* doesn't. 转换为void*不会。 And a conversion involving a user defined conversion is always "less good" than one which doesn't involve a user defined conversion. 并且涉及用户定义的转换的转换总是“不太好”而不涉及用户定义的转换。

The real issue here is that C++ doesn't have a built-in string type, and the string literals don't have type std::string . 这里真正的问题是C ++没有内置字符串类型,字符串文字没有类型std::string The normal solution is to provide an overload for char const* as well: 通常的解决方案是为char const*提供重载:

void f( void* );
void f( std::string const& );
inline void f( char const* p ) { f( std::string( p ) ); }

This additional overload will pick up string literals. 这个额外的重载将获取字符串文字。

(As a general rule: anytime you're overloading: if one of the overloads is for std::string , provide one for char const* as well, if any are for arithmetic types, provide one for int , to catch integral literals, and if any are for a floating point type, provide one for double , to catch floating point literals.) (作为一般规则:任何时候你重载:如果其中一个重载用于std::string ,也为char const*提供一个,如果有任何算术类型,为int提供一个,以捕获整数文字,如果有任何浮点类型,请提供一个double精度数来捕获浮点文字。)

The apparent issue is, as noted by others, that MSVC allows implicit conversion from string literals to non- const char* , and thence to void* . 正如其他人所指出的那样,明显的问题是MSVC允许从字符串文字隐式转换为非const char* ,从而转换为void*

I say apparent because your void* overload should be a void const* overload, as it does not change the pointed to data. 我说明显是因为你的void*重载应该是一个void const*重载,因为它不会改变指向数据。 Doing so will make things 'worse', as calling it with a string literal will now unambiguously select the void const* overload. 这样做会使事情“变得更糟”,因为用字符串文字调用它现在将明确地选择void const* overload。 However this illustrates what is going wrong: "" is a char const(&)[1] (an array of const char ), not a std::string , and char const(&)[1] is closer related to pointers than to std::string . 然而,这说明了什么是错误: ""是一个char const(&)[1] (一个const char数组),而不是一个std::string ,而char const(&)[1]与指针的关系比到std::string Relying on the overload picking std::string over a pointer is fragile, even on gcc, as making your code const correct breaks it! 依赖于指针的重载选择std::string是脆弱的,即使在gcc上,因为使你的代码const正确打破它!

To fix this we can write a greedy overload for std::string . 为了解决这个问题,我们可以为std::string编写一个贪心重载。

template<typename S, typename=typename std::enable_if<std::is_convertible<S,std::string>::value>::type>
void f(S&&s){
  f(std::string{std::forward<S>(s)});
}

with the above two overloads left intact (except const added). 上面两个重载保持完整(除了const添加)。

Or (better) via tag dispatching: 或者(更好)通过标签调度:

void f(void const* v, std::false_type){
  std::cout << "f(void*)\n";
}
void f(std::string const& s, std::true_type){
  std::cout << "f(const std::string &)\n";
}
template<typename T>
void f(T&&t){
  return f(std::forward<T>(t), std::is_convertible<T,std::string>() );
}

both of which are ways to do manual function overload dispatching, biased towards std::string . 这两种方法都是手动函数重载调度,偏向于std::string

live example 实例

Note that std::string literals are now possible in C++, but I would advise against requiring them on the basis of fragility. 请注意,现在可以在C ++中使用std::string文字,但我建议不要在脆弱的基础上要求它们。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM