2011-02-16 128 views
9

鉴于这种人为的例子:C++成员函数链接返回类型和派生类

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

+1

的`base_exception <类派生>你有你的问题的最后是被称为奇异递归模板模式(CRTP)`想法。我认为它不会为您的情况提供解决方案,因为您的异常层次结构中无法使用单个根基类。 – 2011-02-16 17:43:14

+0

@艾米尔:是的,我说这不是一个完美的解决方案,为什么。 – 2011-02-16 17:54:44

+0

我只是指出,为了其他读者的利益,你显示的模式有一个特定的名称。我还给出了另一个原因,为什么(不同于你的)解决方案不起作用。我不打算批评你。 :-) – 2011-02-16 19:46:17

回答

0

你为什么不走了,最简单的方法(也许不是最优雅):

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

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

7

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

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

Defininately包含了一些我知道我在做什么,但是富而另一方面(至少在我的应用程序)的基础没有继承就不能使用类。

最好的问候, 注册查询