2017-03-02 42 views
1

我想通过一个派生类作为一个std :: shared_pointer到一个函数,其参数是基类,它有一个模板。C++ 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的”与共享指针。

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

我可以创建一个“AnyBase”类删除模板参数,但随后会删除某些类型安全的,我在我的实际应用,这是必须到位必须的。

有一件事我考虑是创建新类AnyBasestd::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以及IRuleW)的第二个模板参数的匹配类型与第一个模板类型ITask的匹配类型。这用作用户的API调用,以确保在添加边时,类型在编译时正确排列。

+0

由于您正在铸造'static_pointer_cast',因此适用。 – NathanOliver

+0

理想情况下,我想打电话给doSomething(testval1); ...所以没有铸造任何东西。动态强制转换是为了显示一种方法能够将doSomething函数与模板化的shared_ptr一起使用。 – Jameshobbs

+0

我知道。只要让你知道动态演员阵容是不需要的时候。看起来他们只有一个转换构造函数,如果该对象是临时的。他们不会自动转换左值。 – NathanOliver

回答

3

一个简单的解决方案是更改doSomething以处理更广泛的参数集。

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

编辑:使用您最近的一个例子,我已经写了如何可以达到你想要的东西了更广泛的过载和std::enable_ifstatic_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); 
} 
+0

我可否请求downvote的澄清? –

+0

可能是因为你的回答有投射,而OP不想投。 (而不是向下投票者) – NathanOliver

+0

在shared_ptr的模板参数中删除基类参数将删除所使用的类型安全。另外'std :: shared_ptr ',没有定义函数typeName()。 – Jameshobbs

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>>彼此不相关;一个不是另一个的派生类,所以你不能避免重铸。

+0

这与使用AnyBase类型并删除模板参数类似。这将工作,但我需要模板参数提供的类型安全。不过,我真的很喜欢使用static_pointer_cast,我可能会在我的应用程序的其他地方使用它。真的很酷的东西。 – Jameshobbs

+0

我答案中的最后一段解释了为什么需要演员。静态转换就像类型安全一样,如果'opaque_ptr'不是从'Base '派生的,'base_type'将会失败编译。 –

+0

'doSomething'参数中的类型安全性非常重要。我在我的意思的应用程序中添加了一个示例。 – Jameshobbs

1

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

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

但这变得极为和日益复杂。

+0

如果'Base '可以从普通类型派生,那么这种解决方案可以大大简化,在这种情况下,您可以使用'std :: is_base_of'。 –

+0

这看起来很有趣。我的编辑如何应用上述示例:'template void addRuleEdge(Bookkeeper * bookkeeper,std :: shared_ptr > rule,ITask * consumer)',I不能使用C++ 14,但能够使用'std :: enable_if'而不是'std :: enable_if_t'。 – Jameshobbs

+0

@FrançoisAndrieuxTrue。另外,我可以通过一些想法使'is_derived_from_template_instance_of'更清晰一些。 – Yakk