协变返回类型取决于模板类typedef

时间:2016-02-06 23:11:51

标签: c++ templates c++11 covariance

就像我每次尝试混合泛型编程和多态时一样,我不得不为我的编译器而烦恼。 C ++很有趣,但有多困难(好吧,我的大脑也很复杂,没有用)。

我在下面写了一个简化但类似的情况,我得到相同的编译器错误:

template<typename Object>
class Environment;

class Boat {
};

template<typename Object>
class Worker {
public:
    typedef Object      Object_type;
public:
                        Worker() {}
    virtual             ~Worker() throw() {}
    virtual bool        work() = 0;
    virtual const Environment<Object>*
                        env() const=0;
};

template<typename Object>
class Environment {
public:
    typedef Object      Object_type;
public:
                        Environment() {}
    virtual             ~Environment() throw() {}
    virtual Worker<Object>*   
                        spawnWorker() const=0;
};

template<typename Employee>
class Enterprise;

template<typename Object>
class Painter: public Worker<Object> {
public:
    using typename      Worker<Object>::Object_type;
    typedef Enterprise<Painter<Object_type> >       
                        Environment_type;
public:
                        Painter( const Environment_type *env) : enterprise(env){}
    const Environment_type*
                        env() const override {return enterprise; }
    bool                work() override {return true;}
private:
    const Environment_type* const              
                        enterprise;
};

template<typename Employee>
class Enterprise: public Environment<typename Employee::Object_type> {
public:
    using typename      Environment<typename Employee::Object_type>::Object_type;
public:
                        Enterprise() {}
                        ~Enterprise() throw() {}
    Worker<Object_type>*  
                        spawnWorker() const override { return new Painter<Object_type>(this); }
};

int main() {
    Enterprise< Painter<Boat> > enterprise;
    auto painter=enterprise.spawnWorker();
    painter->work();

    delete painter;

    return 0;
}

g ++给出错误:

test.cpp: In instantiation of ‘class Painter<Boat>’:
test.cpp:50:7:   required from ‘class Enterprise<Painter<Boat> >’
test.cpp:61:33:   required from here
test.cpp:42:25: error: invalid covariant return type for ‘const Environment_type* Painter<Object>::env() const [with Object = Boat; Painter<Object>::Environment_type = Enterprise<Painter<Boat> >; typename Worker<Object>::Object_type = Boat]’
                     env() const override {return enterprise; }
                     ^
test.cpp:16:25: error:   overriding ‘const Environment<Object>* Worker<Object>::env() const [with Object = Boat]’
                     env() const=0;`

和clang ++可能更容易理解:

test.cpp:42:25: error: return type of virtual function 'env' is not covariant with the return type of the function it overrides ('const Environment_type *' (aka 'const Enterprise<Painter<Object_type> > *') is not derived from 'const Environment<Boat> *')
                        env() const override {return enterprise; }
                        ^
test.cpp:50:47: note: in instantiation of template class 'Painter<Boat>' requested here
class Enterprise: public Environment<typename Employee::Object_type> {
                                              ^
test.cpp:61:33: note: in instantiation of template class 'Enterprise<Painter<Boat> >' requested here
    Enterprise< Painter<Boat> > enterprise;
                                ^
test.cpp:16:25: note: overridden virtual function is here
                        env() const=0;
                        ^

如何解决此问题?如果解决方案仍然存在会更好:

  • 保持env()实现的返回类型协变
  • 使编译器从Enterprise
  • 的Employee模板参数推断出Environment的Object模板参数

1 个答案:

答案 0 :(得分:0)

正如评论中所提到的,问题是派生类试图覆盖具有不同返回类型的函数。您可以通过替换Environment_type中的Painter定义来解决此问题:

typedef Environment<Object> Environment_type;

这个修复是否对你的想法有意义,我无法说清楚,但我想这会给你这个想法。

但总的来说设计看起来有点奇怪。这是我的核心问题:

您真的希望Worker成为继承树的根并将其用于运行时多态吗?我问,因为你无法混合使用Worker<boat>Worker<house>的指针。它们是完全不同的东西,它们的虚函数不匹配。

如果确实想要使用运行时多态性,我建议使用非模板基类。例如,一个船舶绘画企业然后只会产生指向通用工人的指针,而不是船画家。如果你需要它来真正产生一个船画家,那么我会说继承(在这种情况下对于企业/环境)是你想要的。

如果确实需要基类(但不是运行时多态),您可能需要查看CRTP。它看起来很奇怪,可能会引发更多问题,但它是一个强大的工具,并提供另一层编译时多态性。

如果您需要某个接口,但不一定是继承链,您可能还需要查看类型擦除。