9

鉴于这个人为的例子:

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

这个想法是使用“成员函数链接”来能够连续调用多个成员函数。(有很多这样的例子;上面是我能想到的最短的例子。我的实际问题是类似的,如下所述。)

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

有没有什么聪明的方法可以解决这个问题并且仍然保持成员函数链接的能力?


实际问题

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

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_exceptioncatch期望 a derived_exception。当然,人类可以判断异常的实际类型是 a 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,那么我们又回到了同样的问题。

4

3 回答 3

7

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

于 2011-02-16T18:15:00.413 回答
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 类不打算在没有继承的情况下使用。

最好的问候,瑞吉

于 2011-05-24T13:35:21.063 回答
0

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

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

那不会解决您的问题还是我错过了什么?

于 2011-02-16T17:46:17.977 回答