用户定义的转换函数和引用类型的强制转换

7
我遇到了以下代码的编译错误:

我遇到了以下代码的编译错误:

class SymbolGroup
{
  std::string d_;

public:
  SymbolGroup(std::string a):d_(a){}

  // explicit operator const std::string&() const { return d_;} // compiles
  explicit operator std::string() const { return d_;} // Does not compile
};

inline
bool operator==(const SymbolGroup& lhs, const SymbolGroup& rhs)
{
  return static_cast<const std::string&>(lhs) ==
    static_cast<const std::string&>(rhs);
}

int main(){

  SymbolGroup a("hello");
  SymbolGroup b("hello");

  if (a==b)
    std::cout << "they are the same\n";

  return 0;
}

如果在用户定义的类型转换行中没有使用 'const' 和 '&',则在使用 --std=c++11 标志的 g++ (4.8) 中无法编译:

错误:无效的引用初始化类型 'std::string& {aka std::basic_string&}',表达式类型为 'const string {aka const std::basic_string}' explicit operator std::string&() const { return d_;}

这段代码可以在 Clang 上两种方式都编译。哪个编译器是正确的?这段代码应该编译吗 operator std::string()


我没有收到任何编译错误。 - Cory Kramer
1
@CoryKramer 你是用没有 'const' 和 '&' 的版本编译的吗? - ENIGMA
1
只是提醒一下 - 如果你在询问代码无法编译的问题,最好将示例写成无法编译的代码,这样人们就可以快速复制和粘贴。 - Barry
好的,在似乎是我第十次回答这个问题之后,我终于把它搞对了。大概吧。 - Barry
2个回答

5

更新 我之前的回答完全错误。抱歉!简而言之,clang接受这段代码是正确的,gcc拒绝它是不正确的。


首先,来自[expr.static.cast]:
表达式e可以使用形如static_cast<T>(e)的static_cast显式转换为类型T,如果声明T t(e);是良好形式的,对于一些虚构的临时变量t(8.5)。
因此,实际上我们正在尝试从类型为SymbolGroup const&的对象显式地直接初始化一个类型为std::string const&的对象。有一个专门关于通过转换函数初始化引用的部分:“直接引用绑定的转换函数初始化”[over.match.ref]:
在满足8.5.3规定的条件下,引用可以直接绑定到glvalue或类prvalue,该glvalue或类prvalue是通过将初始化表达式应用于转换函数得到的。重载分辨率用于选择要调用的转换函数。假设“cv1 T”是要初始化的引用的基础类型,“cv S”是初始化器表达式的类型,其中S是类类型,则候选函数如下所述选择: - 考虑S及其基类的转换函数。那些不是显式的、且满足[...]的转换函数是候选函数。对于直接初始化,那些不被隐藏在S中且产生类型“lvalue reference to cv2 T2”或“cv2 T2”或“rvalue reference to cv2 T2”的显式转换函数,其中T2与T类型相同或可以通过合格转换(4.4)转换为类型T,也是候选函数。
第一部分不适用,因为我们的转换函数是显式的,所以我省略了它。第二部分适用。我们有cv1 T是const std::string,所以我们的转换函数到std::string是一个候选函数,因为std::string可以通过资格转换转换为const std::string
这里gcc是错误的,我提交了bug 66893,得到了我们自己的C++专家和全能好人Jonathan Wakely以及头号Clang开发人员和C++标准编辑Richard Smith的确认(在我彻底尴尬地提交了一个Clang bug之后)。


1
先生,答案非常简单。不要像这样在返回时转换为const引用:
return static_cast<const std::string&>(lhs) == static_cast<const std::string&>(rhs);

将您的类型转换为 std::string:
return static_cast<std::string>(lhs) == static_cast<std::string>(rhs);

享受编程代码的乐趣 :)


1
这并没有回答问题。此外,OP想要避免复制字符串。 - Barry
1
问题在于 (const std::string&)(lhs) == (const std::string&)(rhs); 可以编译通过,而 static_cast<const std::string&>(lhs) == static_cast<const std::string&>(rhs); 则不能。这个差异是否在标准中说明了呢? - dewaffled
据我所知,在C++中使用C风格的转换是很危险的,因为它会让编译器自行选择转换方式。因此,它可以同时执行静态、常量和重新解释转换。也许在你的例子中,它同时使用了静态和常量转换。 - bartop
@bartop 我认为问题在于这种行为是标准规定还是gcc的bug,因为clang可以编译带有static_cast的代码。 - dewaffled

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