【问题标题】:C++: Cast operator overloading and referencesC++:强制转换运算符重载和引用
【发布时间】:2014-10-07 20:18:04
【问题描述】:

C++ 允许通过创建 operator T() 来重载类型转换,其中 T 是我们要转换为的类型。

现在,此功能如何与引用一起使用?例如:

struct Y{ int i; };

struct X{
    Y y;

    operator Y() const { return y; }
};

在这里,我们可以将X 转换为Y,这将简单地返回包含的Y。但是,如果我们想对 Y 引用进行强制转换怎么办。例如,C++ 允许我们这样做:

struct X{
    Y y;

    operator Y&(){ return y; }
    operator const Y&() const { return y; }
};

现在,我们可以将X 转换为Y 引用或const 引用(这也适用于const X)。

第一个例子和第二个例子的语义有什么不同吗?如果我想允许转换为引用,最好的方法是什么?

我可以想象,即使我写了operator T() 没有任何&,C++ 也可能允许强制转换通过引用返回结果(即,当我指定operator T() 时,它可能会以某种方式添加隐式operator T&() 方法) .

例如,我希望下面的代码工作:

int main(){
    X x;
    Y& y = x; // y now references the y inside x
    y.i = 5;
    std::cout << x.y.i << std::endl; // Should print 5 now
}

实现这一目标的最简单方法是什么?

【问题讨论】:

  • 您是在问返回值和引用之间的语义差异,还是返回 const 和非 const 引用之间的语义差异?
  • @Useless:我在问:声明operator T()(没有&amp;)是否意味着强制转换总是按值返回T?还是有一些规则可以在某些情况下返回 T 作为参考?我试图澄清这个问题。

标签: c++ casting reference operator-overloading


【解决方案1】:

声明operator T() 是否意味着强制转换总是按值返回T

是的。

转换运算符的返回类型隐含地正是它们转换为的类型。

[...] 转换函数的类型是“不带参数返回转换类型ID的函数”。

§12.3.2 [class.conv.fct]

【讨论】:

  • 谢谢!但是如果我声明了下面的示例(使用T&amp;const T&amp;),那么我可以省略T 演员表,因为总是可以使用引用+ const 引用吗?
  • 是,因为:“返回“对 cv2 X 的引用”的转换函数返回左值或 x 值,具体取决于“cv2 X”类型的引用类型,因此被认为为此产生 X选择候选函数的过程。” §13.3.1.5 [over.match.conv]
  • @gexicide:如果提供按引用的版本,您几乎肯定要省略T 版本,否则您将需要客户端代码来手动消除大量客户端使用的歧义(即显式编码x.operator Y()x.operator Y&amp;())。
  • 如果是 C++11,对这个返回 Y 的重载的右值引用是明智的@tonyd
  • @Yakk: 有时 - 有时返回的 Y 可能会立即用作表达式的一部分,并且不会绑定任何引用,或者可能会被引用用于计算目的,但不是从....复制过来的。
【解决方案2】:

使用 clang++:

#include <iostream>

class   test
{
public:
  test(int n) : nb{n} {}

public:
  operator int() { return nb; }

  private:
    int nb;
};

int     main(void)
{
  test  t{42};

  int x = t;

  int& ref = t;   // error: non-const lvalue reference to type 'int' cannot bind to a value of unrelated type 'test'
  int&& rref = t; // compiles fine

  std::cout << x << std::endl;
}

这建议您返回一个临时的新值。

帮助您在两种情况之间做出选择的真正问题是:您是否想让人们修改您的内部成员(返回引用)并且该成员的复制成本高(使用 const 引用而不是值) ?

【讨论】:

  • 是的,我想让人们修改会员。但是,我不知道它是否是一个昂贵的副本,因为它的类型是一个模板参数,所以它可以依赖。我只是想给客户最高的灵活性。那么最好的演员阵容是什么?
  • ("intx" 应该是 "int x".. 除此之外) 没有人提到不是很好的风格隐式强制转换运算符重载?只提供模板 setter 和 getter 怎么样(一个重载用于值复制,一个用于 l-reference,一个用于 r-reference)
  • @gexicide 你的第二选择,引用和const引用转换标记为const。
  • @DarioOO 好吧,这取决于您要实现的目标,强制转换运算符比简单的 getter/setter 具有更强的语义,这基本上意味着您的类型足够兼容,因此可以转换为另一种。隐式转换在某些情况下很有意义,尽管如果以错误的方式使用它可能会变得一团糟,但是对于所有 c++ 来说都是如此。谢谢你的错字,它是固定的:)
  • 添加 operator int&amp;() { return nb; }int&amp; ref = t; 也可以。
【解决方案3】:

声明 operator T() 是否意味着强制转换总是按值返回 T

这意味着转换运算符将按照T 的指定返回。无论是引用、指针还是值。例如,给定某种类型(根据 OP)Y:

operator Y();
operator Y&();
operator Y*();

所有(具有 cv 限定的变体)都是有效的用户定义转换。要将Y 用作参考,您需要将其作为参考返回。

所以,是的,转换为不是引用的T 意味着返回类型是一个值。这与 general casting semantics 内联 - 返回一个值。

第一个例子和第二个例子的语义有区别吗?

是的,存在语义差异。它们与其他具有引用与值返回类型的方法中的语义尊重相同。它们会以相同的方式不同,因为以下两种方法不同;

Y get_y_value();
Y& get_y_ref();

备注

在同一个类中混合operator Y();operator Y&amp;(); 可能会导致不明确的重载(至少clang 对此有所抱怨,但gcc 似乎很乐意将两者混合使用)。

我不打算在这里评论哪个更好,因为您的情况的细节还不清楚。不过,一般来说,在实现用户定义的转换时,请始终考虑通常如何执行强制转换,并且可能会更频繁地返回右值。随着 C++11 的出现,也支持 explicit;出于同样的原因,构造函数是explicit(或者如果特别需要,则不是)。

【讨论】:

  • 如果你已经拥有operator Y&amp;(),我认为你不需要operator Y()。也许除非你在 operator Y() 内部有不同的实现。
  • 没错,但两种形式都可用。我最近没有检查合规性,但如前所述,您也可能会遇到模棱两可的问题
猜你喜欢
  • 2012-12-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多