Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/128.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/templates/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 仅使用静态多态性的异构容器_C++_Templates_C++11_Metaprogramming_Containers - Fatal编程技术网

C++ 仅使用静态多态性的异构容器

C++ 仅使用静态多态性的异构容器,c++,templates,c++11,metaprogramming,containers,C++,Templates,C++11,Metaprogramming,Containers,我的目标是实现一个同时接受多种不同类型对象的容器(这里是一组堆栈,每种类型一个)。在运行时,使用void指针(或所有存储类型的公共基类)和运行时类型标识(RTTI),这样做很简单。由于容器将要保存的所有类型在编译时都是已知的,所以使用模板生成此类类可能(也可能不)是可能的。我知道boost::variant已经提供了类似的功能,但它要求存储的类型作为模板参数列出,如boost::variantv 我真正想要的是一个类,它在每次创建一个新的模板专门化(相当于push())时,都会透明地向自身添加一

我的目标是实现一个同时接受多种不同类型对象的容器(这里是一组堆栈,每种类型一个)。在运行时,使用void指针(或所有存储类型的公共基类)和运行时类型标识(RTTI),这样做很简单。由于容器将要保存的所有类型在编译时都是已知的,所以使用模板生成此类类可能(也可能不)是可能的。我知道
boost::variant
已经提供了类似的功能,但它要求存储的类型作为模板参数列出,如
boost::variantv

我真正想要的是一个类,它在每次创建一个新的模板专门化(相当于
push()
)时,都会透明地向自身添加一个匹配的(内部)数据结构。该类的用法如下所示:

int main()
{
    MultiTypeStack foo;
    //add a double to the container (in this case, a stack). The class would
    //..create a matching std::stack<double>, and push the value to the top.
    foo.push<double>(0.1);
    //add an int to the container. In this case, the argument type is deduced.
    //..The class would create a std::stack<int>, and push the value to the top.
    foo.push(123);
    //push a second double to the internal std::stack<double>.
    foo.push<double>(3.14159);
    std::cout << "int: " << foo.top<int>() << "\n";      //"int: 123"
    std::cout << "double: " << foo.top<double>() << "\n";//"double: 3.14159"
    return 0;
}
intmain()
{
多类型栈foo;
//向容器中添加一个double(在本例中为堆栈)
//..创建一个匹配的std::stack,并将该值推到顶部。
foo.push(0.1);
//将int添加到容器中。在这种情况下,将推导参数类型。
//…该类将创建一个std::stack,并将该值推到顶部。
foo.push(123);
//将第二个double推送到内部std::stack。
foo.push(3.14159);

std::cout我有一个实现,它与您所要求的略有不同,但可能适合您。我制作了一个类似列表的结构,当您尝试向其中添加一个新类型的元素时,它可以复制或将自身移动到一个可以包含该新元素类型的封装容器(不同类型)中。(就像复制案例中的持久数据结构)

这是代码。它非常难看,我不打算发布它,但在写这篇文章的时候没有人回答,所以我只能希望有人能帮助它变得更好

//Checks if list (or element) S has element of type T
template<class L, class T> struct HasElem : std::is_same<L,T>{};

template<template<class,class> class Node, class T, class NodeT, class Next>
struct HasElem<Node<NodeT,Next>,T>{
  static constexpr bool value = std::is_same<NodeT,T>::value || HasElem<Next,T>::value;
};
template<template<class> class Leaf, class S, class T> struct HasElem<Leaf<S>,T> :  std::is_same<S,T>{};

//Push type transform
template<class N, class T> struct Push{};
template<template<class,class> class Node, class T, class Next, class U> struct Push<Node<T,Next>,U>{
    typedef Node<U,Node<T,Next>> type;
};

//Node type
template<class T, class Next>
struct Node{
    Node(Next&& nxt) : next(nxt){}
    Node(const Next& nxt) : next(nxt){}

    std::stack<T> st;
    Next next; 

    //Pushing a new type onto the stack
    template<class U> typename std::enable_if<!HasElem<Node,U>::value,typename Push<Node,U>::type>::type
        push(const U& u) &&{ //disallow pushing new types on lvalues
            return typename Push<Node,U>::type(std::move(*this)).push(u);
        }
    //Pushing a new type onto the stack as an lvalue and return a copy
    template<class U> typename std::enable_if<!HasElem<Node,U>::value,typename Push<Node,U>::type>::type
        push_new(const U& u) const{ //cannot overload on && qualifier. Make the name uglier to warn of the cost
            return typename Push<Node,U>::type(*this).push(u);
        }

    //Regular old push
    Node& push(const T& t){ st.push(t); return *this; }
    //Push onto another node in the list
    template<class U> typename std::enable_if<HasElem<Node,U>::value,Node>::type
        push(const U& u){ next.push(u); return *this; }

    template<class U> typename std::enable_if<std::is_same<T,U>::value,U>::type&
        top(){ return st.top(); }
    template<class U> typename std::enable_if<!std::is_same<T,U>::value && HasElem<Node,U>::value,U>::type&
        top(){ return next.top<U>(); } 

};

//The last node. I made it hold data but it doesn't need to
template<class T> struct Leaf{
  std::stack<T> st;

  Leaf& push(const T& t){ st.push(t); return *this; }
  template<class U> Node<U,Leaf> push(const U& u){ 
      return Node<U,Leaf>(std::move(*this)).push(u);
  }

  template<class U> void top(){}
  T& top(){ return st.top(); }
  void pop(){ st.pop(); }
};

主要的缺点是,如果保存中间状态(即保存到变量中),则效率会很低但是,如果您将操作链接在一起,如示例中的
b
,则可以避免该操作。

创建一个
std::unordered\u map
。您键入的访问代码采用该类型并找到适当的条目。然后
unknown
转换为依赖于保存堆栈的
T
的类型

确保
unknown
stack\u holder
的基础,并且
unknown
有一个
virtual
析构函数

这可能不是你想要的,但是C++类型的系统是纯的:以后的表达式不能改变“早期”类型。 如果将类型链接起来,则可以构造更复杂的类型,但这只是列出类型,同时隐藏它们


如果对象是单例对象,一些使用
静态
局部变量的黑客可能会奏效。

尝试使用静态多态性来实现您描述的类型的异构容器的问题是,虽然“容器将要保存的所有类型在编译时都是已知的,”这些信息直到编译过程的后期才可用。事实上,多亏了C++的编译转换单元模型,您只能真正依赖于链接时可用的类型信息,而这只需要发出虚拟调度

实际上,我要说的是,在不直接调用的情况下实现大部分功能的最佳方法是使用Sean Parent在 。它确实在内部依赖于全面的基于继承的动态类型,但它将其全部隐藏起来,并允许通过少量工作根据类型对元素进行分层。扩展@Yakk 建议:

#include <stack>
#include <unordered_map>
#include <typeindex>

class MultiStack
{
    class MultiStackBase
    {
    public:
        virtual ~MultiStackBase () = default;
    };

    template <typename T>
    class MultiStackImpl
        : public MultiStackBase
    {
        std::stack <T> _stack;

    public:
        virtual ~MultiStackImpl () = default;

        template <typename U>
        void push (U&& new_element)
        { _stack.push (std::forward <U> (new_element)); }

        void pop ()
        { _stack.pop (); }

        T& top ()
        { return _stack.top (); }

        const T& top () const
        { return _stack.top (); }
    };

    mutable std::unordered_map <std::type_index, std::unique_ptr <MultiStackBase>> stacks;

protected:
    template <typename T>
    static std::type_index index ()
    { return std::type_index {typeid (T)}; }

    template <typename T>
    MultiStackImpl <T>& stack_cast ()
    {
        if (stacks.count (index <T> ()) == 0)
            stacks [index <T> ()] = std::make_unique <MultiStackImpl <T>> ();
        return dynamic_cast <MultiStackImpl <T>&> (*stacks [index <T> ()]);
    }

    template <typename T>
    const MultiStackImpl <T>& stack_cast () const
    {
        if (stacks.count (index <T> ()) == 0)
            stacks [index <T> ()] = std::make_unique <MultiStackImpl <T>> ();
        return dynamic_cast <const MultiStackImpl <T>&> (*stacks [index <T> ()]);
    }

public:
    template <typename T, typename U>
    void push (U&& new_element)
    { stack_cast <T> ().push (std::forward <U> (new_element)); }

    template <typename T>
    void pop ()
    { stack_cast <T> ().pop (); }

    template <typename T>
    T& top ()
    { return stack_cast <T> ().top (); }

    template <typename T>
    const T& top () const
    { return stack_cast <T> ().top (); }
};


#include <iostream>

int main ()
{
    MultiStack m;

    m.push <int> (42);
    m.push <float> (3.14);

    std::cout << m.top <int> () << std::endl
              << m.top <float> () << std::endl;
}
所以不幸的是,我们使用了动态类型,没有我们想要的模板参数推断(您可以使用推断推送,但我怀疑它会容易出现细微的程序员错误;最好是显式的),但我们得到了期望的行为:一个不枚举类型的多类型堆栈,让编译器替我们确定它们


编辑:我应该指出,与静态类型的实现相比,这种方法有一个潜在的巨大好处(如果这样做是可能的话):使用纯静态实现,每个类型为
MultiStack
的对象对于使用的每种类型都会有一个堆栈;例如,如果在一个函数中的
MultiStack
中使用
std::string
,则生活在另一个函数中的
MultiStack
也会有一个
std::string
堆栈,反之亦然任何给定的多堆栈对象只分配其使用的类型的堆栈。

< P>在C++中有两个演示,描述了如何在静态语言中实现动态容器,如:C++: 1) 静态类型语言中的动态、递归、异构类型:以及

<P>2)动态C+++<

这两个用户也在动态C++文章上为AcCu:

联机文章合作。
<> P>有很多关于如何构建静态C++语言的动态构造的信息。

你不能根据任何一个随机客户端稍后如何使用类型来定义一个类型。没有某种动态代码,你就不能建立一个无界的数据结构。我相信你的用例可能是类似的。至少我需要这样来定义某些闪存数据布局(并且成功地使用了这个习惯用法).
boost::any
是新的
void*
。我想您也可以直接从
MultiStackImpl
对象中提取对
std::stack
的引用,而不是将其隐藏在重复的接口后面。事实上,您可能会替换大部分实现
template<class T, class Next, class U> auto push(Node<T,Next>&& n, const U& u) 
 -> decltype(n.push(u)){
    return n.push(u);
}
template<class T, class Next, class U> auto push(const Node<T,Next>& n, const U& u)
 -> decltype(n.push_new(u)){
    return n.push_new(u);
}

int main(){
    auto b = Leaf<int>().push<int>(42).push<double>(3.14).push<char>('a');
    auto a = push(b,(char*)"Hello"); //Make a copy of b but with "Hello"

    cout << a.top<int>() << " " << a.top<double>() << " " <<
        a.top<char>() << " " << a.top<char*>() << endl;

    cout << b.top<char>() << endl; //The earlier version b still exists
}
#include <stack>
#include <unordered_map>
#include <typeindex>

class MultiStack
{
    class MultiStackBase
    {
    public:
        virtual ~MultiStackBase () = default;
    };

    template <typename T>
    class MultiStackImpl
        : public MultiStackBase
    {
        std::stack <T> _stack;

    public:
        virtual ~MultiStackImpl () = default;

        template <typename U>
        void push (U&& new_element)
        { _stack.push (std::forward <U> (new_element)); }

        void pop ()
        { _stack.pop (); }

        T& top ()
        { return _stack.top (); }

        const T& top () const
        { return _stack.top (); }
    };

    mutable std::unordered_map <std::type_index, std::unique_ptr <MultiStackBase>> stacks;

protected:
    template <typename T>
    static std::type_index index ()
    { return std::type_index {typeid (T)}; }

    template <typename T>
    MultiStackImpl <T>& stack_cast ()
    {
        if (stacks.count (index <T> ()) == 0)
            stacks [index <T> ()] = std::make_unique <MultiStackImpl <T>> ();
        return dynamic_cast <MultiStackImpl <T>&> (*stacks [index <T> ()]);
    }

    template <typename T>
    const MultiStackImpl <T>& stack_cast () const
    {
        if (stacks.count (index <T> ()) == 0)
            stacks [index <T> ()] = std::make_unique <MultiStackImpl <T>> ();
        return dynamic_cast <const MultiStackImpl <T>&> (*stacks [index <T> ()]);
    }

public:
    template <typename T, typename U>
    void push (U&& new_element)
    { stack_cast <T> ().push (std::forward <U> (new_element)); }

    template <typename T>
    void pop ()
    { stack_cast <T> ().pop (); }

    template <typename T>
    T& top ()
    { return stack_cast <T> ().top (); }

    template <typename T>
    const T& top () const
    { return stack_cast <T> ().top (); }
};


#include <iostream>

int main ()
{
    MultiStack m;

    m.push <int> (42);
    m.push <float> (3.14);

    std::cout << m.top <int> () << std::endl
              << m.top <float> () << std::endl;
}
42
3.14