我试图将派生类作为std :: shared_pointer传递给一个函数,该函数的参数是基类,它有一个模板。
以下是一个完整的例子:
template <class T>
class Base {
public:
std::string typeName()
{
int status;
char *realName = abi::__cxa_demangle(typeid(T).name(), 0, 0, &status);
std::string ret(realName);
free(realName);
return ret;
}
};
class Derived : public Base<float> {};
我想称之为“doSomething&#39;使用共享指针。
template <class V>
void doSomethingNotShared(Base<V> *test)
{
std::cout << "Not shared type: " << test->typeName() << std::endl;
};
template <class V>
void doSomething(std::shared_ptr<Base<V>> test)
{
std::cout << "Shared type: " << test->typeName() << std::endl;
};
这是显示我如何使用它并指出编译错误的主要功能。
int main()
{
std::shared_ptr<Derived> testval1 = std::shared_ptr<Derived> (new Derived());
doSomething(testval1); // <- Compilation error
Derived *testval2 = new Derived();
doSomethingNotShared(testval2);
std::shared_ptr<Base<float>> testCast = std::dynamic_pointer_cast<Base<float>>(testval1); // Would rather not have to do this if there is another way ...
doSomething(testCast); // <- No error runs fine
}
有没有办法让这项工作与doSomething(testval1);
一起使用?我希望不必使用dynamic_pointer_cast
(或任何其他类型的转换机制),只需使用Derived。
主要错误是:std::shared_ptr<Derived> is not derived from std::shared_ptr<Base<V> >
我可以创建一个&#34; AnyBase&#34;删除模板参数的类,但那将删除我在实际应用程序中的一些类型安全性,这是必须的。
我考虑的一件事是在新类AnyBase
和std::shared_ptr<AnyBase>
之间创建映射,然后使用函数doSomethingNotShared
来处理类型安全性,并使用查找映射以获取shared_ptr。 (见下文)
std::map<AnyBase *, std::shared_ptr<AnyBase>>
编辑:
从我的应用程序检查类型安全的示例:
template <class V, class W, class X>
void addRuleEdge(Bookkeeper<V> *bookkeeper, std::shared_ptr<IRule<V, W>> rule, ITask<W, X> *consumer)
在这种情况下,我希望第一个模板类型Bookkeeper
中的V
IRule
与IRule
的第二个模板参数的匹配类型之间存在匹配类型(W
)具有ITask
的第一个模板类型。这用作API调用,用户可以确保在编译时正确排列类型的边缘。
答案 0 :(得分:3)
一个简单的解决方案是更改doSomething
以处理更广泛的参数集。
template <class V>
void doSomething(std::shared_ptr<V> test)
{
std::cout << "Shared type: " << test->typeName() << std::endl;
};
编辑:使用您的最新示例,我已经编写了一个示例,说明如何通过更广泛的重载以及std::enable_if
或static_assert
来实现您的目标。我提供了空类以允许编译示例。
#include <memory>
template<class V>
class Bookkeeper {};
template<class V, class W>
class IRule {};
// Some rule class derived from IRule compatible with Bookkeepr
class RealRule : public IRule<int, float> {};
template<class W, class X>
class ITask {};
// Some task class derived from ITask, compatible with RealRule
class RealTask : public ITask<float, double> {};
// Some task class derived from ITask, not compatible with RealRule
class BadTask : public ITask<int, double> {};
template <class V, class Rule, class W, class X>
typename std::enable_if<std::is_base_of<IRule<V, W>, Rule>::value, void>::type
addRuleEdge(Bookkeeper<V> *bookkeeper, std::shared_ptr<Rule> rule, ITask<W, X> *consumer)
{
// Do work
}
int main()
{
Bookkeeper<int> my_book_keeper;
auto my_rule = std::make_shared<RealRule>();
RealTask my_task;
// Compiles
addRuleEdge(&my_book_keeper, my_rule, &my_task);
BadTask bad_task;
// Won't compile (no matching overload)
addRuleEdge(&my_book_keeper, my_rule, &bad_task);
}
如果您只是想在使用错误的类型时收到通知,您也可以选择使用static_assert
。
template <class V, class Rule, class W, class X>
void addRuleEdge(Bookkeeper<V> *bookkeeper, std::shared_ptr<Rule> rule, ITask<W, X> *consumer)
{
static_assert(std::is_base_of<IRule<V, W>, Rule>::value, "Type mismatch!");
// Do work
}
int main()
{
Bookkeeper<int> my_book_keeper;
auto my_rule = std::make_shared<RealRule>();
RealTask my_task;
// Compiles
addRuleEdge(&my_book_keeper, my_rule, &my_task);
BadTask bad_task;
// Won't compile (error "Type mismatch!")
addRuleEdge(&my_book_keeper, my_rule, &bad_task);
}
答案 1 :(得分:1)
这似乎可以通过一些额外的工作来实现。
首先,首先在基类中添加一个类型,使其模板参数别名:
template <class T>
class Base {
public:
typedef T type;
// The rest of your base class is as it is before.
};
现在,稍微调整doSomething(),获取一个不透明的共享指针,然后找出它来自的基类,然后重新构建它:
template <class opaque_ptr>
void doSomething(std::shared_ptr<opaque_ptr> param)
{
typedef typename opaque_ptr::type base_type;
auto test = std::static_pointer_cast<Base<base_type>>(param);
// Now your test is a std::shared_ptr<Base<T>>, proceed as before.
如果你坚持要传递std::shared_ptr<Base<V>>
,你必须对其进行输入转换。 std::shared_ptr<Derived>
和std::shared_ptr<Base<V>>
彼此无关;一个不是另一个的派生类,所以你不能避免重铸。
答案 2 :(得分:1)
shared_ptr<Base<V>>
是一种可以从shared_ptr<Derived>
转换的类型,但它们不相关。
template<template<class...>class Z>
struct is_derived_from_template_of_helper {
template<class...Ts>
constexpr std::true_type operator()(Z<Ts...>*) const { return {}; }
constexpr std::false_type operator()(...) const { return {}; }
};
template<template<class...>class Z, class T>
using is_derived_from_template_of_t =
decltype(
is_derived_from_template_of_helper<Z>{}(
std::declval<std::decay_t<T>*>()
)
);
现在我们可以这样做:
template <class X,
class=std::enable_if_t<
is_derived_from_template_instance_of_t<Base, X>{}
>
>
void doSomething(std::shared_ptr<X> test)
{
std::cout << "Shared type: " << test->typeName() << std::endl;
}
template<template<class...>class Z>
struct derived_from_template_args_tags_helper {
template<class...Ts>
constexpr std::tuple<tag_t<Ts>...> operator()(Z<Ts...>*) const { return {}; }
};
template<template<class...>class Z, class T, std::size_t I>
using derived_from_template_arg = typename std::tuple_element<I, decltype(derived_from_template_args_tags_helper<Z>{}(std::declval<std::decay_t<T>*>()))>::type::type;
template <class X,
class=typename std::enable_if<
std::is_same<
derived_from_template_arg<Base, X, 0>, float
>{}
>::type
>
void doSomething(std::shared_ptr<X> test)
{
std::cout << "Shared float type: " << test->typeName() << std::endl;
}
但这变得非常复杂。