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

发布于 2024-10-17 20:35:45 字数 1907 浏览 3 评论 0原文

给出这个人为的示例:

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

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

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

是否有任何聪明的方法来解决这个问题并仍然保持进行成员函数链接的能力?


实际问题

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

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 需要一个 <代码>派生异常。当然,人类可以辨别出异常的实际类型是衍生异常,但编译器显然无法辨别。

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

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

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

我相信实际上确实解决了问题,但这不是一个完美的解决方案,因为如果另一个类 more_driven_exception code> 派生自 衍生异常,那么我们又回到了同样的问题。

Given this contrived example:

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

the idea is to use "member-function chaining" to be able to call more than one member-function in a row. (There are many examples of this; the above is the shortest one I could think of for the purpose of asking this question. My actual problem is similar and is described below.)

The problem is that if a derived class adds its own chaining member-functions but you call a base class's member function first, you get a base-class reference that of course won't work for calling a derived class's member-function.

Are there any clever ways to solve this problem and still maintain the ability to do member-function chaining?


The Actual Problem

My actual problem is that my base class is an exception and my derived class is a class derived from the base exception. For those classes also, I want to use 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'
  }
}

The problem is that set_something() returns base_exception but the catch expects a derived_exception. Of course a human can tell that the actual type of the exception is a derived_exception but the compiler apparently can't tell.

That's the problem I'm really trying to solve, i.e., how to have a base exception class be able to set optional parameters on the exception object yet return an instance of the derived type. The point_2d example I gave above is (I believe) a smaller and simpler version of the same problem for people to understand and that a solution to the smaller problem will also solve my actual problem.

Note that I did consider making base_exception a template and pass in the derived type like:

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

I believe that in fact does solve the problem, but it's not a perfect solution because if another class more_derived_exception derives from derived_exception, then we're back to the same problem.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

讽刺将军 2024-10-24 20:35:45

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

What you're looking for is the Named Parameter Idiom, which I am copying from this StackOverflow answer. Rather than return a reference to the actual object, you return a reference to a special parameter object, and rely on a constructor for your exception object to do an implicit conversion once all the parameters are filled in. It's quite clever, really.

安静被遗忘 2024-10-24 20:35:45

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

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 但另一方面(至少在我的应用程序中)基类并不意味着在没有继承的情况下使用。

此致,
雷吉

Hi there
I just had a similar Problem and here My Solution:

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

Defininately contains some I know what I'm doing foo but on the other hand (at least in my application) the Base class is not meant to be used without inheritence.

Best regards,
Regi

秋日私语 2024-10-24 20:35:45

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

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

这不会解决你的问题还是我会错过一些东西?

Why don't you go for the simplest approach (maybe not the most elegant):

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

Wouldn't that solve your problem or do I miss something?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文