繁体   English   中英

从具有泛型返回类型的crtp的基类调用派生类中的函数

[英]Calling function in derived class from base class of crtp with generic return type

我正在创建一个库,允许用户存储指针,然后检索指针。
我有一个存储泛型类型指针的类。

template <typename generic_type>
class A 
{
public:
    A() {}
    A(generic_type *value) : data(value) {}
    generic_type *getData()
    {
        return data;
    }

private:
    generic_type *data;
};

我还想将这些类模板的实例存储在一个向量中,所以我从一个基类继承。

class B 
{
};

template <typename generic_type>
class A : public B
{
...
};

class test_class
{
};

std::vector<B *> list;
// -------- example -------
// test_class *test = new test_class();
// list.push_back(new A<test_class>(test));
// list.push_back(new A<test_class>(test));
// list.push_back(new A<test_class>(test));

从列表中检索时,我将获得一个指向基类的指针。
要在派生类A中调用函数,必须进行强制转换。

// example
B *bp = list.at(0); 
A<test_class> *ap = static_cast<A<test_class> *>(bp);

我不希望用户手动必须自己转换基指针,但能够在A调用getData()函数,它将返回基于generic_type的实际数据。

// preferred API usage
B *bp = list.at(0); // they will get B pointer in another way but shown here for simplicity
test_class *t = bp->getData();

为了从父类调用子函数,我找到了CRTP,它允许我在A调用函数;但是现在我无法将它存储在向量中,因为B将被模板化。 所以我从另一个引用此帖子中的代码的类派生了B
这允许我将它存储在向量中但我似乎无法找到一种方法来调用具有泛型返回类型的函数并返回它。

class C
{
public:
  virtual void func() = 0;
};
template <typename derived>
class B : public C
{
public :
  void func() // cant change to templated return type as virtual function cant be templated
  {
    derived *p = static_cast<derived *>(this);
    p->getData(); // how to return the value from this ?
  }
};

template <typename generic_type>
class A : public B<A<generic_type>>
{
...
};

std::vector<C *> list;
// -------- example -------
// this allows me to do  
C *cp = list.at(0);
cp->func(); // will call getData() indirectly
            // but am unable to get the returned data

我不熟悉较新的功能或设计模式,因为我几年没有使用过c ++。 是否有可能获得返回的数据? 或者我可以使用不同的方法或模式来实现所需的API使用场景?

您永远不会想到用户必须指定他们希望接收的类型。 编译器需要在编译时知道getData的返回类型是什么,所以你必须在编译时决定你正在操作哪个具体A实例。

但是,有一种方法可以使“指定类型”比手动静态转换更方便:在B为任何指针提供隐式转换。

class B 
{
public:
    virtual ~B() = default;

    template<class T>
    operator T*();
};

template <typename generic_type>
class A : public B
{
  // ...
};

template<class T>
B::operator T*()
{
    A<T>* a = dynamic_cast<A<T>*>(this);
    if (!a)
      return nullptr;
    return a->getData();
}

// Usage:

std::vector<std::unique_ptr<B>> list;
list.emplace_back(new A<test_class>);
list.emplace_back(new A<int>);
list.emplace_back((A<double>*)nullptr);

test_class* out1 = *list[0];   // Returns the correct ptr
test_class* out2 = *list[1];   // Returns nullptr (incorrect type)
double* out3 = *list[2];       // Returns nullptr (data value is null)

https://godbolt.org/z/NT1AF8

此转换运算符假定,当我们尝试将某个对象xB转换为某些T*x的动态类型为A<T*> 它尝试向下转换并在成功时返回getData() ,否则返回nullptr

原则上这很好用,但是你应该意识到隐含的转换更好地使用或者根本不使用 - 它们可能导致非常令人惊讶的行为。 您可能还希望(SFINAE-)防范创建A<A<something>> <A <something A<B>A<B> ,因为这听起来很头疼。

让它与cv说明符(即const / volatile )一起工作将需要更多的工作(包括指定A<const int>是否应该是一个东西)。


你也可以通过编写一个免费的访问函数来实现手动static_cast和上面的隐式转换之间的中间路径

template<class T>
T* getData(B& b)
{
  // See operator T* implementation above.
}

它将被用作double* y = getData<double>(x); 我更喜欢这个解决方案的库接口。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM