C ++模板多态性障碍 [英] C++ Templates polymorphism obstacle

查看:109
本文介绍了C ++模板多态性障碍的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

接口:

 模板< class T& 
class Interface {
public:
typedef T Units;
virtual T get()= 0;
};

实施1:

 code> class Implementation1:public Interface< float> {
public:

float get(){
return 0.0f;
}

};

Implementation2:

  class Implementation2:public Interface< int> {
public:

int get(){
return 0;
}

};

容器(含错误):

  class Container {
private:
Interface * floatGetter;
int n;
Timer :: Units * array;

public:
容器(接口* floatGetter,int n){
this-> floatGetter = floatGetter;
this-> n = n;
array = new Timer :: Units [n];
}

〜Container(){

}

};

更多细节,我有一个模板接口和一个派生类,一些其他类接受派生类的对象,但它将对象作为接口(换句话说,依赖注入)。但是这个类中的接口类型由接口实现定义。如何在C ++中实现此想法?



Edit1:



/ p>

 接口< float> myInterface1 = new Implementation1(); 
Interface< int> myInterface2 = new Implementation2();
container container1 = new Container(myInterface1,10);
container container2 = new Container(myInterface2,10);

我需要该容器从其实现中理解接口模板参数。

解决方案

确定,首先,在这里解释问题。所需要的是一个接口,它定义了一个虚拟方法,用于获得一个模板类型的值。因为我们想要的是一个接口,get方法必须是虚拟的。另一方面,我们希望能够返回不同的类型,所以我们想要to化它。然而,一个虚拟方法不能被神化,因为编译器不知道该方法的哪个实例包括在vtable中。



一个解决方案是做什么做在问题中,即templetize的接口类。模板类型的一个重要属性是同一类的不同实例化是完全不同的类型。他们不共享一个共同的基础,他们不可以互相转换。我们根本不能在正则函数中有一个 Interface< Generic> 指针,它们的get()方法被调用。考虑这一点:接口模板类型的每个实例化对于get()方法具有不同的签名。这意味着,当该方法被调用时,不同的事情必须发生在堆栈上。如果所有它都是一个 Interface< Generic> 指针,编译器如何知道要调用的get()方法的哪个版本(如何为函数调用准备堆栈) 。



我可以想出两个一般解决方案。


  1. p>删除所有模板mumbo-jumbo并使get()方法返回一个类型擦除的对象,如boost :: variant或boost :: any。纠正我,如果我错了这里(*),但boost :: variant像一个联合,记住分配的联合类型,而boost :: any像一个void *,但它记住它指向什么类型。这个解决方案路径意味着两件事:
    a)返回的对象的类型将在运行时解析,并且在操作这些类型时会有一些开销。
    b)Interface的子类必须管理其中一个类型删除的对象,使它们更复杂。


  2. jumbo到极端,并且指向接口对象总是在一个神圣化的上下文中,以便编译器在这些上下文的实例化期间生成正确的函数调用。我给了一个例子,下面这个路径。该示例创建用于将不同类型的接口<>对象集合在一起的容器,同时使得能够向其应用模板化的函数(将其称为访问者)是正确的。注意,在该示例中,具有不同类型参数的接口对象实际上保存在该容器类中的不同std ::列表中,因此在运行时,不需要解析它们的类型。


免责声明:以下是过度的...



具有不同模板参数的接口模板类。我使用了std :: list来保存实例,但您可以更改它。

 #include< boost / fusion /container/vector.hpp> 
#include< boost / fusion / algorithm.hpp>
#include< boost / mpl / transform.hpp>
#include< boost / mpl / contains.hpp>
#include< boost / utility / enable_if.hpp>
#include< boost / type_traits / add_reference.hpp>
#include< list>
#include< algorithm>
#include< iostream>

使用命名空间boost;

template< class T>
class Interface {
public:
typedef T Units;
virtual T get()= 0;
};

类实现1:public Interface< float> {
public:

float get(){
return 0.0f;
}

};

类Implementation2:public Interface< int> {
public:

int get(){
return 5;
}

};

template< class element>
struct to_list {
typedef std :: list< Interface< element> *>类型;
};

template< class elementVector>
struct to_containers {
typedef typename mpl :: transform< elementVector,to_list< mpl :: _ 1> > :: type type;
};

class Container {
typedef fusion :: vector< int,float> AllowedTypes;
typename to_containers< AllowedTypes> :: type containers;

public:
template< class type> typename enable_if< mpl :: contains< allowedTypes,type>,void> :: type
/ * void * / add(Interface< type / * included in AllowedTypes * />& floatGetter){
fusion :: deref(fusion :: find< typename to_list< type> :: type>(containers))
/ *< type> container * /。push_back(& floatGetter);
}

template< class functional>
void apply(functional f){
fusion :: for_each(containers,applyFunctional< functional>(f));
}

private:
template< class functional>
struct applyFunctional {
functional f;
applyFunctional(functional f):f(f){}
template< class T> void operator()(T& in)const {
std :: for_each(in.begin(),in.end(),f);
}
};

};

struct printValueFunctional {
template< class element>
void operator()(Interface< element> * in)const {
std :: cout<<Hi,my value is:< <\\\
;
}
};

int main(){

Implementation1 impl1;
Implementation2 impl2;
接口< float> & myInterface1 = impl1;
Interface< int> & myInterface2 = impl2;
集装箱集装箱;
container.add(myInterface1);
container.add(myInterface2);
container.apply(printValueFunctional());
return 0;
}

输出为:

 您好,我的价值是:5 
我的价值是:0

$好吧,这真的是对大多数应用程序的一个巨大的过度,但你问:)



如果你只是想要一个接口,可以返回不同的东西,你也可以考虑boost.variant。上面的例子对于它使用的所有静态多态性都是非常有价值的。



编辑:David指出了一些重要的东西,它可能是一个陷阱,否则假设。这个容器并不真正保持项目插入的顺序。函数调用的顺序可能不会按照项的插入顺序发生,即假设迭代将按照随机顺序。



*)boost :: variant和boost :: any都已讨论这里 p>

Interface:

template <class T>
class Interface{
    public:
    typedef T Units;
    virtual T get() = 0;
};

Implementation1:

class Implementation1: public Interface<float> {
    public:

    float get() {
       return 0.0f;
    }

};

Implementation2:

class Implementation2: public Interface<int> {
    public:

    int get() {
       return 0;
    }

};

Container (with errors):

class Container{
    private:
    Interface* floatGetter;
    int n;
    Timer::Units* array;

    public:
    Container(Interface* floatGetter, int n) {
        this->floatGetter= floatGetter;
        this->n = n;
        array = new Timer::Units[n];
    }

    ~Container() {

    }

};

For more details, I have a template interface and a derived class from this interface without template. Some other class take an object of the derived class but it takes the object as an interface (in other words, dependency injection). But the type of the interface in this class is defined by the interface implementation. How to implement this idea in C++?

Edit1:

Example:

Interface<float> myInterface1 = new Implementation1();
Interface<int> myInterface2 = new Implementation2();
Container container1 = new Container(myInterface1, 10);
Container container2 = new Container(myInterface2, 10);

I need that container understands interface template argument from its implementation.

解决方案

OK, first, an explanation of the problem here. What's required is an interface, that defines a virtual method, used to get a value with a templated type. Since what we want is an interface, the get method has to be virtual. On the other hand, we would like to be able to return different types, so we want to templetize it. However, a virtual method can not be templetized, because the compiler wouldn't know which instantions of that method to include in the vtable.

One solution is to do what's done in the question, i.e. templetize the interface class. An important property of template types is that different instantiations of the same class are completely different types. They don't share a common base, and they're not convertible to each other. We simply can not have an Interface<Generic> pointer going around in regular functions, with their get() methods being called. Consider this: Every instantion of the Interface template type has a different signature for the get() method. This means that while that method is being called, different things have to happen on the stack. How could the compiler know which version of the get() method to call (how to prepare the stack for the function call) if all it has is a Interface<Generic> pointer.

I can think of two general solutions to that problem.

  1. Remove all template mumbo-jumbo and make the get() method return a type-erased object, such as boost::variant or boost::any. Correct me if I'm wrong here(*), but boost::variant is like a union that remembers which type of the union is assigned, while boost::any is like a void *, but it remembers what type it's pointing to. This solution path implies two things: a) The types of the returned objects will be resolved at runtime, and there will be some overhead while manipulating these types. b) The child classes of Interface will have to manage one of these type-erased objects, making them more complicated.

  2. Take the template mumbo-jumbo to the extreme and refer to Interface objects always in a templetized context, so that the compiler generates the right function calls during the instantiations of those contexts. I gave an example below which follows this path. The example creates a container for holding together different types of Interface<> objects, while enabling the application of templetized functionals (is it correct to call this generally "visitors"?) to them. Note that in that example, the Interface objects with different type parameters are actually kept in different std::lists in that container class, so in the runtime, there's no need to resolve their types.

Disclaimer: What follows is an overkill...

Here's how you can have a container of the "interface" template class with different template arguments. I've used an std::list to keep the instances, but you can change it.

#include<boost/fusion/container/vector.hpp>
#include<boost/fusion/algorithm.hpp>
#include<boost/mpl/transform.hpp>
#include<boost/mpl/contains.hpp>
#include<boost/utility/enable_if.hpp>
#include<boost/type_traits/add_reference.hpp>
#include<list>
#include<algorithm>
#include <iostream>

using namespace boost;

template <class T>
class Interface{
    public:
    typedef T Units;
    virtual T get() = 0;
};

class Implementation1: public Interface<float> {
    public:

    float get() {
       return 0.0f;
    }

};

class Implementation2: public Interface<int> {
    public:

    int get() {
       return 5;
    }

};

template<class element>
struct to_list {
    typedef std::list<Interface<element> *> type;
};

template<class elementVector>
struct to_containers {
    typedef typename mpl::transform<elementVector,to_list<mpl::_1> >::type type;
};

class Container{
    typedef fusion::vector<int,float> AllowedTypes;
    typename to_containers<AllowedTypes>::type containers;

public:
    template<class type> typename enable_if<mpl::contains<AllowedTypes,type>,void>::type 
    /*void*/ add(Interface< type/*included in AllowedTypes*/ > & floatGetter) {
        fusion::deref(fusion::find<typename to_list<type>::type >(containers))
            /*<type> container*/.push_back(&floatGetter);
    }

    template<class functional>
    void apply(functional f) {
        fusion::for_each(containers,applyFunctional<functional>(f));
    }

private:
    template<class functional>
    struct applyFunctional {
        functional f;
        applyFunctional(functional f): f(f){}
        template<class T> void operator()(T & in) const {
            std::for_each(in.begin(), in.end(),f);
        }
    };

};

struct printValueFunctional {
    template<class element>
    void operator()(Interface<element> * in) const {
        std::cout<<"Hi, my value is:"<<in->get()<<"\n";
    }
};

int main() {

    Implementation1 impl1;
    Implementation2 impl2;
    Interface<float> &myInterface1 = impl1;
    Interface<int> &myInterface2 = impl2;
    Container container;
    container.add(myInterface1);
    container.add(myInterface2);
    container.apply(printValueFunctional());
    return 0;
}

And the output is:

Hi, my value is:5
Hi, my value is:0

Well, this really is a huge overkill for most applications, but you asked for it :)

If you just want an interface, that can return different things, you could also consider boost.variant. The example above is truly valuable for all the static polymorphism it uses.

EDIT: David has pointed something important, it might be a pitfall, if you, for some reason, assume otherwise. This container doesn't really stay true to the order of the item insertions. The order of your functional calls might not happen in the order of the insertions of the items, i.e., assume that the iteration will be in a "random" order.

(*) boost::variant and boost::any are discussed here

这篇关于C ++模板多态性障碍的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆