Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/142.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/mongodb/13.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 在模板派生类中用协变返回类型重写虚函数_C++_Inheritance_Types - Fatal编程技术网

C++ 在模板派生类中用协变返回类型重写虚函数

C++ 在模板派生类中用协变返回类型重写虚函数,c++,inheritance,types,C++,Inheritance,Types,我想重写模板派生类中的虚拟函数。但是,我想使用派生类作为返回类型。以下是相应的代码: class Abstract { public: virtual Abstract* allocate() const = 0; }; template <typename Derived> class Base : public Abstract { public: Derived* allocate() const override { return new

我想重写模板派生类中的虚拟函数。但是,我想使用派生类作为返回类型。以下是相应的代码:

class Abstract {
  public:
    virtual Abstract* allocate() const = 0;
};
template <typename Derived>
class Base : public Abstract {
  public:
    Derived* allocate() const override {
      return new Derived;
    }
};
class Concrete : public Base<Concrete> {
  public:
};
int main() {
  Concrete c;
  delete c.allocate();
}

allocate
函数移到
Concrete
类中可以解决这个问题,但在创建多个具体类时会导致代码重复。有没有办法让编译器知道
派生的
实际上是从
抽象的
派生的?

您可以模拟协变返回类型:

这里的要点是类
重新引入
。没有它,
allocate
将覆盖虚拟函数。使用它,它隐藏了继承的函数

#include <iostream>

class Abstract {
  public:
    virtual Abstract* allocate() const = 0;
};

namespace details{
    class Reintroduce {};

    template <typename Derived>
    class AllocImpl : public Abstract
    {
    public:
        Abstract* allocate() const override {
            return new Derived;
        }
    };
}


template <typename Derived>
class Base : public details::AllocImpl<Derived> 
{
  public:
    Derived* allocate(details::Reintroduce = {}) const 
    {
      return static_cast<Derived*>(details::AllocImpl<Derived>::allocate());
    }
};

class Concrete : public Base<Concrete> {
  public:
};


int main() {
  Concrete c;
  delete c.allocate();
}
#包括
类摘要{
公众:
虚拟抽象*allocate()常量=0;
};
命名空间详细信息{
类重新引入{};
模板
类AllocImpl:公共摘要
{
公众:
抽象*分配()常量重写{
返回新导出的;
}
};
}
模板
类库:公共详细信息::AllocImpl
{
公众:
派生*分配(详细信息::重新引入={})常量
{
返回静态_cast(details::AllocImpl::allocate());
}
};
混凝土类别:公共基础{
公众:
};
int main(){
混凝土c;
删除c.allocate();
}

如果我们能够在没有
AllocImpl
的情况下实现它,那将是一件好事,但这将使调用变得模棱两可。

问题是
具体的
基础中不完整(无论是
基础还是
)(与任何CRTP一样)

所以编译器还不知道
Concrete
继承自
Abstract

可能的解决方案是不使用CRTP,而是使用常规继承:

template <typename Base>
class Derived : public Base {
  public:
    Derived* allocate() const override {
      return new Derived;
    }
};
class ConcreteImpl : public Abstract {
  public:
};

using Concrete = Derived<ConcreteImpl>;
模板
派生类:公共基{
公众:
派生的*分配()常量重写{
返回新导出的;
}
};
类ConcreteImpl:公共摘要{
公众:
};
使用混凝土=衍生材料;

修改引入类型标记以区分不同的
分配
函数,我们可以创建一个更简单的解决方案。诀窍在于,我们在
Abstract
类中引入type标记,并重写
allocate
方法,而不提供默认参数(以避免歧义)。以下解决方案的缺点是,我们需要能够更改
抽象
类中
分配
方法的签名,这可能并不总是可行的

class BaseTag {};
class Abstract {
  public:
    virtual Abstract* allocate(BaseTag = {}) const = 0;
};

template <typename Derived>
class Base : public Abstract {
  public:
    Abstract* allocate(BaseTag) const override { return allocate(); }
    Derived* allocate() const {
      return new Derived;
    }
};

class Concrete : public Base<Concrete> {
  public:
};

int main() {
  Concrete c;
  delete c.allocate();

  Abstract* a = &c;
  delete a->allocate();
}
classbasetag{};
类摘要{
公众:
虚拟抽象*分配(BaseTag={})常量=0;
};
模板
类库:公共摘要{
公众:
抽象*分配(BaseTag)常量重写{return allocate();}
派生的*分配()常量{
返回新导出的;
}
};
混凝土类别:公共基础{
公众:
};
int main(){
混凝土c;
删除c.allocate();
摘要*a=&c;
删除->分配();
}

这也是个好主意。这很好,因为这样你就不需要演员阵容了。但是,如果您想返回封装在“智能指针”中的协变返回类型指针,该怎么办?不幸的是,智能指针不是协变的:-/您可能仍然有两种方法:返回指针的协变方法,一个非虚拟类,它调用虚拟类将结果封装在智能指针中。在您的示例中,您使用的是“中介”类,与“CRTP”相反。好主意。
class BaseTag {};
class Abstract {
  public:
    virtual Abstract* allocate(BaseTag = {}) const = 0;
};

template <typename Derived>
class Base : public Abstract {
  public:
    Abstract* allocate(BaseTag) const override { return allocate(); }
    Derived* allocate() const {
      return new Derived;
    }
};

class Concrete : public Base<Concrete> {
  public:
};

int main() {
  Concrete c;
  delete c.allocate();

  Abstract* a = &c;
  delete a->allocate();
}