C++隐式转换

28

我最近回答了一个问题,What other useful casts can be used in C++,得到了一些评论指出我的C++转换理解存在问题。为了澄清这个问题,请考虑以下代码:

#include <string>

struct A {
    A( const std::string & s ) {}
};

void func( const A & a ) {
}

int main() {
    func( "one" );                  // error
    func( A("two") );           // ok
    func( std::string("three") );   // ok
}
我的断言是第一个函数调用是错误的,因为从const char *到A没有转换。虽然从字符串到A有一个转换,但使用它将涉及多个转换。我的理解是这不被允许,而且g++ 4.4.0和Comeau编译器似乎证实了这一点。在Comeau中,我得到以下错误:
"ComeauTest.c", line 11: error: no suitable constructor exists 
      to convert from "const char [4]" to "A"
      func( "one" );                    // error
如果你能指出我哪里错了,无论是在这里还是在原回答中,请尽快指出并参考C++标准。而根据C++标准的回答似乎是:对于单个值,最多只会隐式应用一个用户定义的转换(构造函数或转换函数)。感谢Abhay提供的引用。

对不起,我之前在评论中的回答都是错的。我读到了“过载分辨率用于选择要调用的用户定义转换”。然后我告诉自己:“好吧,那么它将解析为A(string const&)并将其传递给“one””,但是我完全没有考虑到13.3.3.1.2中所说的内容:“用户定义的转换序列由初始标准转换序列后跟用户定义转换(12.3)后跟第二个标准转换序列组成。”但是,“one”->字符串不会是标准转换序列,而需要另一个用户定义的转换序列! - Johannes Schaub - litb
只要我不失去理智,就好了 :-) - anon
很好的问题!讨论暗示std::string不是语言的一部分,对它的转换是“用户定义”的。至少这是我的理解,如果我错了,请纠正我。如果问题更明确一些会更好。std::string的确切状态可能对于老的C++程序员来说非常清晰,但对于这个世纪学习该语言的人来说并不容易意识到。 - just somebody
@just 正确 - std::string 只是一个库类 - 你自己可以编写一个与它完全相同的类,因此语言不会为它指定任何特殊处理。 - anon
4个回答

13

我认为 sharptooth 的答案很明确。C++ 标准 (SC22-N-4411.pdf) 第 12.3.4 节“转换”明确指出,只允许一种隐式用户定义的转换。

1 类对象的类型转换可以通过构造函数和转换函数来指定。这些转换称为用户定义的转换,用于隐式类型转换(第 4 部分),初始化(8.5)和显式类型转换(5.4、5.2.9)。

2 只有在它们是无歧义的情况下才应用用户定义的转换(10.2、12.3.2)。在消除歧义后,遵守访问控制规则(第 11 部分)。

3 [ 注意:请参阅第 13.3 节以了解在函数调用中使用转换的讨论以及下面的示例。结束注意 ]

4 对于单个值,最多可以隐式应用一种用户定义的转换(构造函数或转换函数)。


我感到有必要分享一下我为什么喜欢在特定场景中使用隐式转换:即当我想将一个简单类型的变量与执行相关函数的类耦合时。除了隐式转换外,我还在类中提供了一个返回值的函数,就像std::string提供c_str()一样。这样,您可以在需要额外的类功能时使用它,并且在其他地方可以保持极轻量级,同时保持相关功能在一起。 - moodboom

9

众所周知:是的,你是正确的。

但是,由于这个问题/答案可能会成为stackoverflow上C++隐式转换的参考点,我想补充一下:对于模板参数,规则是不同的。

不允许对用于模板参数推导的参数进行隐式转换。这可能看起来非常明显,但仍然可能导致微妙的怪异行为。

例如,std::string加法运算符。

 std::string s;
 s += 67;    // (1)
 s = s + 67; // (2)

(1) 编译并正常工作,operator+= 是一个成员函数,模板字符参数已经通过实例化 std::string 对 s 进行了推导(变为 char)。因此允许隐式转换 (int -> char),结果是 s 包含 67 的 char 等效值,例如在 ASCII 中这将变为 'C'。
(2) 因为 operator+ 被声明为自由函数,并且在此处使用了模板字符参数 进行 推导,所以会产生编译器错误。

9

没错,只允许进行一次隐式转换。

如果需要进行两次转换,可以通过结合转换运算符和带参数的构造函数来实现,但这会在VC++中引发C4927警告,警告原因是“非法转换;已经隐式应用了多个用户定义的转换”。


7

C++程序设计语言(第四版)(第18.4.3节)指出,只有一级用户定义的隐式转换是合法的。

只有一个级别的用户定义的隐式转换是合法的。

“用户定义”的部分听起来好像如果某些转换是在本机类型之间进行,则可以允许多个隐式转换。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接