C++成员函数链接返回类型和派生类

Pau*_*cas 9 c++ member-functions derived-class

鉴于这个人为的例子:

struct point_2d {
  point_2d& x( int n ) {
    x_ = n;
    return *this;
  }

  point_2d& y( int n ) {
    y_ = n;
    return *this;
  }

  int x_, y_;
};

struct point_3d : point_2d {
  point_3d& z( int n ) {
    z_ = n;
    return *this;
  }

  int z_;
};

int main() {
  point_3d p;
  p.x(0).y(0).z(0); // error: "point_2d" has no member named "z"
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

我们的想法是使用"member-function chaining"来连续调用多个成员函数.(有很多例子;上面是我能想到的最短的一个问题.我的实际问题是类似的,如下所述.)

问题是,如果派生类添加了自己的链接成员函数,但是首先调用基类的成员函数,则会得到一个基类引用,当然不能用于调用派生类的成员函数.

有没有聪明的方法来解决这个问题,仍然保持成员函数链的能力?


实际问题

我的实际问题是我的基类是一个异常,我的派生类是从基本异常派生的类.对于那些类,我也想使用成员函数链接:

class base_exception : public std::exception {
  // ...
  base_exception& set_something( int some_param ) {
    // ...
    return *this;
  }
};

class derived_exception : public base_exception {
  // ...
};

int main() {
  try {
    // ...
    if ( disaster )
      throw derived_exception( required_arg1, required_arg2 )
            .set_something( optional_param );
  }
  catch ( derived_exception const &e ) {
    // terminate called after throwing an instance of 'base_exception'
  }
}
Run Code Online (Sandbox Code Playgroud)

问题是set_something()返回base_exception但是catch期望a derived_exception.当然,人类可以告诉异常的实际类型是一个,derived_exception但编译器显然无法分辨.

这就是我真正想要解决的问题,即如何使基本异常类能够在异常对象上设置可选参数,并返回派生类型的实例.point_2d我上面给出的例子(我相信)是一个更小更简单的同一问题版本,供人们理解,小问题的解决方案也将解决我的实际问题.

请注意,我确实考虑制作base_exception模板并传递派生类型,如:

template<class Derived>
class base_exception {
  // ...
  Derived& set_something( int some_param ) {
    // ...
    return *this;
  }
};
Run Code Online (Sandbox Code Playgroud)

我相信事实上确实解决了这个问题,但它并不是一个完美的解决方案,因为如果另一个类more_derived_exception派生出来derived_exception,那么我们就会回到同样的问题.

Mar*_*som 7

你正在寻找的是命名参数成语,我从这个StackOverflow答案中复制了.您不是返回对实际对象的引用,而是返回对特殊参数对象的引用,并且在填充所​​有参数后依赖于异常对象的构造函数来执行隐式转换.它非常聪明,真的.


sst*_*stn 0

为什么不采用最简单的方法(也许不是最优雅的方法):

if ( disaster )
{
    derived_exception e = derived_exception( required_arg1, required_arg2 );
    e.set_something( optional_param );
    throw e;
}
Run Code Online (Sandbox Code Playgroud)

这不能解决你的问题还是我错过了什么?