从具有通用 return 类型的 crtp 的基 class 派生 class 中调用函数
Calling function in derived class from base class of crtp with generic return type
我正在创建一个允许用户存储指针并稍后检索指针的库。
我有一个 class 存储通用类型的指针。
template <typename generic_type>
class A
{
public:
A() {}
A(generic_type *value) : data(value) {}
generic_type *getData()
{
return data;
}
private:
generic_type *data;
};
我还想将这些 class 模板的实例存储在一个向量中,所以我继承了一个基础 class。
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));
从列表中检索时,我将得到一个指向基 class.
的指针
要调用派生 class A 中的函数,必须强制转换它。
// example
B *bp = list.at(0);
A<test_class> *ap = static_cast<A<test_class> *>(bp);
我不希望用户必须自己手动转换基指针,但能够调用 A
中的 getData()
函数,这将 return 实际数据基于在 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();
四处寻找从父 class 调用子函数的过程,我发现了 CRTP,它允许我调用 A
中的函数;但现在我无法将其存储在向量中B
将被模板化。所以我从另一个 class 中派生出 B
引用来自这个 post.
的代码
这允许我将它存储在一个向量中,但我似乎无法找到一种方法来调用具有通用 return 类型和 return 的函数。
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++ 了。
是否可以获取 returned 数据?或者我可以使用不同的方法或模式来实现所需的 API 使用场景吗?
您永远无法回避这样一个事实,即用户必须指定他们希望收到的类型。编译器需要在编译时知道 getData
的 return 类型是什么,所以你必须在编译时决定你正在操作哪个具体的 A
实例。
但是,有一种方法可以使此 "specifying the type" 比手动静态转换更方便:在 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)
此转换运算符假定,当我们尝试将某些对象 x
从 B
转换为某些 T*
时,x
的动态类型是 A<T*>
。它尝试向下转换,如果成功则 returns getData()
,否则 nullptr
。
这在原则上工作得很好,但是您应该意识到隐式转换最好谨慎使用或根本不使用 - 它们会导致非常令人惊讶的行为。您可能还想 (SFINAE-) 防止有人创建 A<A<something>>
或 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);
一样使用。对于库界面,我更喜欢这种解决方案。
我正在创建一个允许用户存储指针并稍后检索指针的库。
我有一个 class 存储通用类型的指针。
template <typename generic_type>
class A
{
public:
A() {}
A(generic_type *value) : data(value) {}
generic_type *getData()
{
return data;
}
private:
generic_type *data;
};
我还想将这些 class 模板的实例存储在一个向量中,所以我继承了一个基础 class。
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));
从列表中检索时,我将得到一个指向基 class.
的指针
要调用派生 class A 中的函数,必须强制转换它。
// example
B *bp = list.at(0);
A<test_class> *ap = static_cast<A<test_class> *>(bp);
我不希望用户必须自己手动转换基指针,但能够调用 A
中的 getData()
函数,这将 return 实际数据基于在 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();
四处寻找从父 class 调用子函数的过程,我发现了 CRTP,它允许我调用 A
中的函数;但现在我无法将其存储在向量中B
将被模板化。所以我从另一个 class 中派生出 B
引用来自这个 post.
的代码
这允许我将它存储在一个向量中,但我似乎无法找到一种方法来调用具有通用 return 类型和 return 的函数。
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++ 了。 是否可以获取 returned 数据?或者我可以使用不同的方法或模式来实现所需的 API 使用场景吗?
您永远无法回避这样一个事实,即用户必须指定他们希望收到的类型。编译器需要在编译时知道 getData
的 return 类型是什么,所以你必须在编译时决定你正在操作哪个具体的 A
实例。
但是,有一种方法可以使此 "specifying the type" 比手动静态转换更方便:在 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)
此转换运算符假定,当我们尝试将某些对象 x
从 B
转换为某些 T*
时,x
的动态类型是 A<T*>
。它尝试向下转换,如果成功则 returns getData()
,否则 nullptr
。
这在原则上工作得很好,但是您应该意识到隐式转换最好谨慎使用或根本不使用 - 它们会导致非常令人惊讶的行为。您可能还想 (SFINAE-) 防止有人创建 A<A<something>>
或 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);
一样使用。对于库界面,我更喜欢这种解决方案。