简体   繁体   English

具有模板基类的C ++ std :: shared_pointer

[英]C++ std::shared_pointer with template base class

I am trying to pass a derived class as a std::shared_pointer to a function whose parameter is the base class, which has a template. 我试图将派生类作为std :: shared_pointer传递给参数为基类的函数,该函数具有模板。

Here is a full example: 这是一个完整的示例:

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> {};

The function I would like to call is the 'doSomething' with the shared pointer. 我想调用的函数是带有共享指针的“ doSomething”。

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;
};

And here is the main function to show how I'd like to use it and pointing out the compilation error. 这是主要功能,以显示我要如何使用它并指出编译错误。

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
}

Is there any way to make this work with doSomething(testval1); 有什么办法可以使它与doSomething(testval1); ? I am hoping to not have to use dynamic_pointer_cast (or any other type of casting mechanism) and just use Derived. 我希望不必使用dynamic_pointer_cast (或任何其他类型的转换机制),而只需使用Derived。

The main error is: std::shared_ptr<Derived> is not derived from std::shared_ptr<Base<V> > 主要错误是: std::shared_ptr<Derived> is not derived from std::shared_ptr<Base<V> >

I could create a "AnyBase" class that removes the template parameter, but then that would remove some of the type safety that I have in place in my actual application, which is a must. 我可以创建一个删除模板参数的“ AnyBase”类,但这将删除我在实际应用程序中必须具备的某些类型安全性。

One thing I'm considering is create a mapping between the new class AnyBase and std::shared_ptr<AnyBase> , then use the function doSomethingNotShared for handling the type safety, and using the lookup mapping to get the shared_ptr. 我正在考虑的一件事是在新类AnyBasestd::shared_ptr<AnyBase>之间创建一个映射,然后使用doSomethingNotShared函数处理类型安全,并使用查找映射获取shared_ptr。 (see below) (见下文)

std::map<AnyBase *, std::shared_ptr<AnyBase>> 

edit: 编辑:

Example for checking type safety from my application: 从我的应用程序检查类型安全性的示例:

template <class V, class W, class X>
void addRuleEdge(Bookkeeper<V> *bookkeeper, std::shared_ptr<IRule<V, W>> rule, ITask<W, X> *consumer)

In this case I want to have matching types between the Bookkeeper for V in the first template type of IRule and matching types for the second template parameter of IRule ( W ) with the first template type for ITask . 在这种情况下,我希望在IRule的第一个模板类型中的Bookkeeper for VIRuleW )的第二个模板参数与ITask的第一个模板类型之间具有匹配类型。 This is used as an API call for users to ensure when adding an edge the types line up correctly at compile-time. 用作用户的API调用,以确保添加边缘时类型在编译时正确排列。

A simple solution is to change doSomething to handle a broader set of arguments. 一个简单的解决方案是更改doSomething以处理更广泛的参数集。

template <class V>
void doSomething(std::shared_ptr<V> test)
{
    std::cout << "Shared type: " << test->typeName() << std::endl;
};

Edit: Using your latest example, I've written an example for how you can achieve what you want with a broader overload and std::enable_if or static_assert . 编辑:使用最新示例,我编写了一个示例,说明如何通过更广泛的重载和std::enable_ifstatic_assert I've supplied empty classes to allow the example to compile. 我提供了空类以允许示例进行编译。

#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);
}

You can also choose to use static_assert if you simply want to be notified when the wrong types are used. 如果您只想在使用错误的类型时收到通知,也可以选择使用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);
}

This looks like achievable with just a little bit of additional work. 仅需一点点额外的工作就可以实现。

First, begin by adding a type to the base class that aliases its template parameter: 首先,首先在基类中添加别名作为其模板参数的类型:

template <class T>
class Base {
 public:

      typedef T type;

    // The rest of your base class is as it is before.
};

Now, tweak doSomething() slightly, to take an opaque shared pointer, and then figure out the base class it came from, then re-cast it: 现在,稍微调整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.

If you insist on passing std::shared_ptr<Base<V>> around, you have to type-convert it. 如果您坚持要传递std::shared_ptr<Base<V>> ,则必须对其进行类型转换。 std::shared_ptr<Derived> and std::shared_ptr<Base<V>> are not related to each other; std::shared_ptr<Derived>std::shared_ptr<Base<V>>彼此不相关; one is not a derived class of the other one, so you cannot avoid recasting. 一个不是另一个的派生类,因此您无法避免重铸。

A shared_ptr<Base<V>> is a type that can be converted-from a shared_ptr<Derived> but they are otherwise unrelated. 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>*>()
    )
  );

now we can do this: 现在我们可以这样做:

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;
}

live example . 现场例子

If you want to get types out , 如果您想输入类型

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;
}

but this gets exceedingly and increasingly complex. 但这变得越来越复杂。

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

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