C++ visual studio中涉及void*、string和const char[]的意外重载解析

C++ visual studio中涉及void*、string和const char[]的意外重载解析,c++,C++,我使用visual studio编译器(在VS2010和VS2012中测试)获得了以下意外的重载解析行为 最简单的例子: #include <iostream> #include <string> void f(void *) { std::cout << "f(void*)\n"; } void f(const std::string &) { std::cout << "f(const std::string &am

我使用visual studio编译器(在VS2010和VS2012中测试)获得了以下意外的重载解析行为

最简单的例子:

#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!");
}
预期输出:

> f(const std::string &)
使用GCC编译(使用4.6.3测试)生成预期输出

如果我注释掉f()的“const std::string&”版本,visual studio将在/W4上愉快地编译,而没有任何警告,而GCC会发出以下错误(如预期的那样):“从“const void*”到“void*”的转换无效[-fppermissive]”

有人知道为什么VisualStudio会这样做,基本上选择const cast重载,而不是转换为字符[]的std::string吗


有没有办法禁止这种行为,或者至少让VS生成一个警告?

我不明白为什么会发生意外。
char的转换
const[]
std::string
涉及用户定义的转换; 到
void*
的转换不起作用。以及涉及 用户定义的转换总是比 不涉及用户定义的转换

真正的问题是C++没有内置字符串。 类型,并且字符串文本没有类型
std::string
。 通常的解决方案是为
char const*
以及:

这个额外的重载将拾取字符串文本

(作为一般规则:任何时候你超载:如果 重载用于
std::string
,为
char const*
提供一个重载 如果有算术类型,请为
int
,用于捕获整型文字,如果有的话,用于浮点数 点类型,为双精度提供一个,以捕获浮点 文本。)

作为Microsoft特定的C++行为:

特定于Microsoft的

在VisualC++中,可以使用字符串文字来初始化指针 非常量字符或wchar\t。这在C代码中是允许的,但是 在C++98中已弃用,在C++11中已删除

设置
/Zc:strictStrings
(禁用字符串文字类型转换)编译器选项时,如果将字符串文字转换为非常量字符,则可能导致编译器发出错误


对于VS 2013版本(例如),微软C++文档中的字符串文字是使用C代码作为“代码”> char < /COR> >的非常量数组。

< P>显然,问题是,MSVC允许从字符串文字到非代码> const >代码> char */COD>隐式转换。然后转到
void*

我之所以说显然是因为
void*
重载应该是
void const*
重载,因为它不会更改指向的数据。这样做会让事情变得“更糟”,因为使用字符串文本调用它现在将明确地选择
void const*
重载。然而,这说明了问题所在:
是一个
字符常量(&)[1]
(一个
常量字符的数组),而不是
std::string
字符常量(&)[1]
与指针的关系比
std::string
更密切。即使在gcc上,依靠指针上的重载拾取
std::string
也是脆弱的,因为使代码
const
正确会破坏它

为了解决这个问题,我们可以为
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)});
}
这两种方法都是执行手动函数重载调度的方法,偏向于
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)});
}


< >注意 > STD::String 文字现在在C++中是可能的,但是我建议不要基于脆弱性来要求它们。

+ 1(特别是对于一般规则),但是我能看到为什么它是意外的;因为它还依赖于从字符串文字到
char*
的不幸的
const
-droping转换。问题是转换为指向非const的指针,这是不允许的。@Angew但const droping仍然不是用户定义的转换。(在更一般的情况下,我可以理解为什么这是意外的,因为从逻辑上讲,字符串文字和
std::string
都是字符串,人们不认为转换是必要的。唉,出于历史原因…@JamesKanze我知道,但即使人们意识到字符串文字不是
std::string
s,MSVC在没有任何警告的情况下应用
常量
-drop仍然令人困惑。@interjay我的理解是不允许这样做:“只有在存在明确的适当指针目标类型[…]时才考虑此转换”(C++03,§4.2/2)。我将“适当的指针目标类型”解释为窄字符串文本的
char*
(并且只有
char*
)。无论如何,C++11完全禁止它,即使是对于一个
char*
@dyp好的点。在程序员意图方面模棱两可?是的,也许吧。这要么是一个相当微妙的重载解决方案(因此容易出错),要么不是表达程序员的意图。在
coid f(std::string const&s,std::true_type){blah}中还有另一个打字错误(我应该修复这些类型的打字错误吗?@DyP当然,但我真的应该编译我的答案。:)我需要找到一个好方法在我的手机上设置一个编译器,或者一个在手机上运行良好的web编译器(其中很多都有奇怪的编辑框,不能很好地与触摸屏浏览器配合使用)。
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)});
}
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>() );
}