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

时间:2011-02-16 17:29:00

标签: c++ derived-class member-functions

鉴于这个人为的例子:

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;
}

这个想法是使用“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'
  }
}

问题是set_something()会返回base_exception,但catch需要derived_exception。当然, human 可以告诉异常的实际类型是derived_exception,但编译器显然无法分辨。

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

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

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

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

3 个答案:

答案 0 :(得分:7)

您要找的是Named Parameter Idiom,我是从this StackOverflow answer复制的。您不是返回对实际对象的引用,而是返回对特殊参数对象的引用,并且在填充所​​有参数后依赖于异常对象的构造函数进行隐式转换。它非常聪明,真的。

答案 1 :(得分:1)

你好 我刚遇到了类似的问题,在这里我的解决方案:

template<class DerivedOptions>
class SomeOptions
{
  private:
    DerivedOptions* derived;
    int param1_;
  public:
    SomeOptions()
    {
        derived = reinterpret_cast<DerivedOptions*>(this);
    }

    DerivedOptions & set_some_options(int param1)
    {
        param1_ = param1;
        return *derived;
    }
};

struct MoreOptions: public SomeOptions<MoreOptions>
{
  private:
    int more_;
  public:
    MoreOptions & set_more_options(int more)
    {
        more_ = more;
        return *this;
    }
};

肯定包含一些我知道我正在做什么foo但另一方面(至少在我的应用程序中)Base类并不意味着没有继承使用。

祝你好运, REGI

答案 2 :(得分:0)

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

if ( disaster )
{
    derived_exception e = derived_exception( required_arg1, required_arg2 );
    e.set_something( optional_param );
    throw e;
}

这不会解决你的问题或者我会错过什么吗?