具有抽象继承的后缀运算符++

Posted

技术标签:

【中文标题】具有抽象继承的后缀运算符++【英文标题】:postfix operator++ with abstract inheritance 【发布时间】:2015-10-20 15:28:21 【问题描述】:

我想在结构层次结构(抽象基 A 和子 B)中实现前缀和后缀运算符++。当只在基类中实现前缀时,这很好用。但是,在子类中实现后缀版本时(因为它不能在抽象类中实现)它不起作用。

struct A 
    virtual A& operator++()std::cout << "A: prefix ++" << std::endl; return *this;
    virtual void foo() = 0;
;

struct B : A 
    void foo() override ;
    //virtual B& operator++() override std::cout << "B: prefix ++" << std::endl; return *this;
    //B operator++(int i) std::cout << "B: postfix ++" << std::endl; return *this;
;

int main(int argc, const char * argv[]) 
    B b;
    ++b; // Compile error here if I implement postfix in B
    return 0;

问题是我想避免重复代码,并且由于所有派生类都将以相同的方式使用 operator++,因此最好不要在各自的类中全部实现它们。使用抽象类的全部意义在于避免这种情况!

我的问题是:解决这个问题的最优雅的方法是什么?

编辑。错误消息:不能增加类型“B”的值

【问题讨论】:

错误信息是什么? Where's the error again? 在增加 B 类型的对象时确定要返回 A 类型的对象吗? @antonio 嗯……没想到!真的。我当然可以在每个子类中实现这两种方法。唯一让我烦恼的是重复代码,但也许没有更好的方法来做到这一点? 不要忘记运算符只是语法糖:仅使用它们有效地执行递增操作。我个人很难理解如何以有意义的方式继承这样的操作,但也许其他人可以提供这样的观点 【参考方案1】:

问题是派生类中的operator++ 将名称隐藏在基类中。尝试将以下内容添加到B

using A::operator++;

您可能会发现很难使后增量函数以多态方式运行。协变返回类型将不起作用。

Live demo.

【讨论】:

这行得通!但是,我担心在递增 B 对象时返回 A& 毕竟不是一个好主意... 在我的示例中,B 类定义了我想要的后缀,但也不必定义前缀。不过,还是关心退货的事情,不过这个问题或许没有很好的答案。 @Myone 你可以用use CRTP 代替抽象基类。 CRTP 看起来非常优雅!感谢您的提示。 它可能看起来很优雅,但是由于 B 和 C 是从 Templates 类继承的,它们不是从 SAME 类继承的。如果你试图调用一个需要类的函数,如果类型为 Incremementable,它不会起作用,因为没有“Incremementable”的通用实现,只有 Incremementable 和 Incremementable 是完全独立的基类。【参考方案2】:

既然你要求一种更优雅的方式来做到这一点,我认为最优雅的方式是尽可能地向你的类的消费者隐藏多态接口的细节。

一个包含多态概念的类就可以解决问题,并允许您轻松安全地实现后增量:

#include <memory>
#include <iostream>

// this is our non-polymorphic handle class. In this case each handle
// owns a discrete object. We could change this to shared-handle semantics by using shared_ptr if desired.
struct poly_thing

    // this is the polymorphic concept (interface)
    struct concept 
        virtual void increment() = 0;
        virtual std::unique_ptr<concept> clone() const = 0;
        virtual ~concept() = default;
    ;

    poly_thing(std::unique_ptr<concept> p_concept)
    : _impl(std::move(p_concept))
    

    // must override copy constructor because of unique_ptr
    poly_thing(const poly_thing& r)
    : _impl(r._impl->clone())
    

    poly_thing(poly_thing&& r) = default;

    // must override copy constructor because of unique_ptr
    poly_thing& operator=(const poly_thing& r)
    
        _impl = r._impl->clone();
        return *this;
    

    poly_thing& operator=(poly_thing&& r) = default;

    //
    // here is our sane non-polymorphic interface.
    //        
    poly_thing operator++(int) 
        std::cout << "operator++(int)" << std::endl;
        auto clone_p = _impl->clone();
        _impl->increment();
        return poly_thing  std::move(clone_p) ;
    

    poly_thing& operator++() 
        std::cout << "operator++()" << std::endl;
        _impl->increment();
        return *this;
    

    std::unique_ptr<concept> _impl;
;

// an implementation (model) of the concept
struct implementation_a : poly_thing::concept 
    std::unique_ptr<poly_thing::concept> clone() const override
    
        std::cout << "cloning an a" << std::endl;
        return std::make_unique<implementation_a>(*this);
    

    void increment() override 
        std::cout << "incrementing an a" << std::endl;
        // implementation here
    
;

// a model derived from a model    
struct implementation_b : implementation_a 
    std::unique_ptr<poly_thing::concept> clone() const override
    
        std::cout << "cloning a b" << std::endl;
        return std::make_unique<implementation_b>(*this);
    

    // you can now choose whether to implement this
    void increment() override 
        // implementation here
        std::cout << "incrementing a b" << std::endl;
    
;

// a small test
auto main() -> int

    poly_thing a(std::make_unique<implementation_a>());
    auto aa1 = a++;
    auto aa2 = ++a;

    poly_thing b(std::make_unique<implementation_b>());
    auto bb1 = b++;
    auto bb2 = ++b;

    return 0;

预期输出:

operator++(int)
cloning an a
incrementing an a
operator++()
incrementing an a
cloning an a
operator++(int)
cloning a b
incrementing a b
operator++()
incrementing a b
cloning a b

【讨论】:

以上是关于具有抽象继承的后缀运算符++的主要内容,如果未能解决你的问题,请参考以下文章

抽象类

面向对象几大原则

为啥继承的受保护运算符=()具有公共访问权限

c ++ - 具有继承的未声明标识符(运算符ostream)[重复]

Scala的继承和抽象类

Java1关键字/数据类型/标识符/运算符,if/switch/for/while,数组,属性/方法,可变参数,构造器,this/super/继承,抽象,初始化,接口,多态,权限修饰符