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_Variadic - Fatal编程技术网

C++ 可变模板数据结构

C++ 可变模板数据结构,c++,templates,c++11,variadic,C++,Templates,C++11,Variadic,我有一个菜单类,它的选项是T类型的项,它可能有菜单类型的子菜单(不限制嵌套子菜单的深度) 不正确,因为菜单我试图创建一个简单的int选项菜单,其中包含字符串选项的子菜单,但它甚至不会编译,因为子菜单的类型是menu,与类模板不匹配。它也没有意义,因为菜单将从其choose()函数返回int,但进入其子菜单将返回一个字符串。这里需要什么 我只需要有人指出如何开始。我知道这似乎属于CodeReview,但在那里他们只想检查我的代码,如果它已经工作了,但在这里,我的推广尝试远没有工作(甚至还没有开始)

我有一个
菜单
类,它的选项是T类型的项,它可能有
菜单
类型的子菜单(不限制嵌套子菜单的深度)

不正确,因为
菜单我试图创建一个简单的int选项菜单,其中包含字符串选项的子菜单,但它甚至不会编译,因为子菜单的类型是
menu
,与类模板不匹配。它也没有意义,因为
菜单
将从其
choose()
函数返回int,但进入其子菜单将返回一个字符串。这里需要什么

我只需要有人指出如何开始。我知道这似乎属于CodeReview,但在那里他们只想检查我的代码,如果它已经工作了,但在这里,我的推广尝试远没有工作(甚至还没有开始),所以我需要呼吁这里的专家就如何开始

更新: 根据gmbeard的建议,我使用了以下简化代码(真正的菜单类将有选项的链接列表,用户将通过输入进行选择)。但也有缺点

#include <iostream>
#include <string>

struct Visitor {
    virtual void visit (int&) = 0;
    virtual void visit (std::string&) = 0;
    virtual void visit (char& c) = 0;
};

struct ChooseVisitor : Visitor {
    std::pair<int, bool> chosenInt;
    std::pair<std::string, bool> chosenString;
    std::pair<char, bool> chosenCharacter;
    virtual void visit (int& num) override {
        chosenInt.first = num;
        chosenInt.second = true;
    }
    virtual void visit (std::string& str) override {
        chosenString.first = str;
        chosenString.second = true;
    }
    virtual void visit (char& c) override {
        chosenCharacter.first = c;
        chosenCharacter.second = true;
    }
};

template <typename...> struct Menu;

template <typename T>
struct Menu<T> {
    struct Option {
        T item;
        void accept (ChooseVisitor& visitor) {visitor.visit(item);}
    };
    Option* option;  // Assume only one option for simplicity here.
    ChooseVisitor choose() const {
        ChooseVisitor visitor;
        option->accept(visitor);
        return visitor;
    }
    void insert (const T& t) {option = new Option{t};}
};

// A specialization for the Menu instances that will have submenus.
template <typename T, typename... Rest>
struct Menu<T, Rest...> {  // Menu with T as its options type.
    struct Option {
        T item;
        Menu<Rest...>* submenu;  // Submenu with the first type in Rest... as its options type.
        void accept (ChooseVisitor& visitor) {visitor.visit(item);}
    };
    Option* option;
    ChooseVisitor choose() const {
    // In reality there will be user input, of course.  The user might not choose to enter a submenu,
    // but instead choose from among the options in the current menu.
        ChooseVisitor visitor;
        if (option->submenu)  
            return option->submenu->choose();
        else
            option->accept(visitor);
        return visitor;
    }
    void insert (const T& t, Menu<Rest...>* submenu = nullptr) {option = new Option{t, submenu};}
}; 

int main() {
    Menu<int, std::string, char> menu;
    Menu<std::string, char> submenu;
    Menu<char> subsubmenu;
    subsubmenu.insert('t');
    submenu.insert ("", &subsubmenu);
    menu.insert (0, &submenu);
    const ChooseVisitor visitor = menu.choose();
    if (visitor.chosenInt.second)
        std::cout << "You chose " << visitor.chosenInt.first << ".\n";  // Do whatever with it.
    else if (visitor.chosenString.second)
        std::cout << "You chose " << visitor.chosenString.first << ".\n";  // Do whatever with it.
    else if (visitor.chosenCharacter.second)
        std::cout << "You chose " << visitor.chosenCharacter.first << ".\n";  // Do whatever with it.
}

最大的问题是,
ChooseVisitor
需要不断更新所有可能的菜单选项类型(最终可能会有数百个数据成员和重载),更不用说可怕的if检查以获得所需的返回项了。但所选物品需要储存,而不仅仅是短期使用。我欢迎改进意见。

一个解决方案是创建一些
菜单的局部专门化,以打开可变参数包

首先,创建模板类

// Can be incomplete; only specialized versions will be instantiated...
template<typename... Args> class Menu;
为了简洁起见,这是类的简化版本,但如果添加嵌套的
选项
类,同样的原则仍然适用

您可以使用类似的技术通过重载非成员函数在子菜单中递归

template<typename T>
void
print_menu(Menu<T> const& menu)
{
  for(auto i : menu.items) {
    std::cout << i << std::endl;
  }
}

template<typename... T>
void
print_menu(Menu<T...> const& menu)
{
  for(auto i : menu.items) {
    std::cout << i << std::endl;
  }
  print_menu(menu.submenu);
}

int
main(int, char*[])
{
  Menu<int, std::string> menu{};
  menu.items.emplace_back(1);
  menu.submenu.items.emplace_back("42");
  print_menu(menu);
  ...
}
print_menu
函数类似,您可以定义两个
choose_menu
函数重载

template<typename... Types, typename Visitor>
void
choose_menu(Menu<Types...> const& menu, Visitor const& visitor)
{
  for(auto i : menu.items) {
    if(/* is menu item chosen? */) {
      visitor(i);
      return;
    }
  }
  choose_menu(menu.Submenu, visitor);
}

template<typename Type, typename Visitor>
void
choose_menu(Menu<Type> const& menu, Visitor const& visitor)
{
  for(auto i : menu.items) {
    if(/* is menu item chosen? */) {
      visitor(i);
      return;
    }
  }
}
模板
无效的
选择菜单(菜单常量和菜单、访客常量和访客)
{
用于(自动i:菜单项){
如果(/*是否选择了菜单项?*/){
访客(一);
返回;
}
}
选择菜单(menu.Submenu,visitor);
}
模板
无效的
选择菜单(菜单常量和菜单、访客常量和访客)
{
用于(自动i:菜单项){
如果(/*是否选择了菜单项?*/){
访客(一);
返回;
}
}
}
这将像这样使用

Menu<int, std::string> menu{};
...
choose_menu(menu, ChooseVisitor{});
菜单{};
...
选择_菜单(菜单,选择查看器{});

对于
choose()
函数,要得出您的想法有点困难,但您应该能够调整以上内容以适应大多数情况。

我有一点感觉,这里的设计有些过头了

正如您所猜测的,这是一个使用Boost变体的版本

我简化了一些事情。我特别喜欢使用初始值设定项列表构造函数,因此您可以简单地递归构造菜单树,如下所示:

Menu menu = MenuT<int> { 
    { "one",   1 },
    { "two",   2 },
    { "three", 3 },

    { "forty-two", 42, 
            Menu(MenuT<std::string> {
                {"Life, The Universe And Everything", "LtUae"},
                {"Dent", "Arthur", 
                    Menu(MenuT<bool> {
                        {"yes", true},
                        {"no", false},
                    })
                },
            })
    },
};

#include <string>
#include <vector>
#include <boost/optional.hpp>
#include <boost/variant.hpp>
#include <iostream>

template <typename> struct MenuT;

using Menu = boost::make_recursive_variant < 
        boost::recursive_wrapper<MenuT<int>>,
        boost::recursive_wrapper<MenuT<std::string>>,
        boost::recursive_wrapper<MenuT<bool>>
    >::type;

template <typename T> struct MenuT {
    struct Option {
        std::string           name;
        T                     item;
        boost::optional<Menu> submenu;

        Option(std::string name, T t, boost::optional<Menu> submenu = boost::none)
            : name(name), item(t), submenu(submenu)
        { }

        T choose() const;
        void print(int n) const;
    };

  private:
    template <typename U> friend struct MenuT;
    friend struct visitors;

    std::vector<Option> options;
    //boost::optional<Menu&> parent; // TODO e.g. link_parents_visitor
    enum ChoosingType { Normal, Remove };

  public:
    enum SpecialPosition : size_t { END_OF_MENU = size_t(-1) };

    MenuT() = default;
    MenuT(std::initializer_list<Option> options) : options(options) {}

    void insert(const std::string &itemName, const T &t, boost::optional<Menu> submenu = boost::none, size_t pos = END_OF_MENU) {
        auto it = (pos == END_OF_MENU 
                ? options.end() 
                : std::next(options.begin(), std::min(pos, options.size())));

        options.emplace(it, itemName, t, submenu);
    }

    T choose() const { return chosenItem().first; }
    int print() const;

  private:
    std::pair<T, int> chosenItem(ChoosingType = Normal) const;
};

struct visitors {
    struct simple : boost::static_visitor<>
    {
        void operator()(Menu& m) const { boost::apply_visitor(*this, m); }

        template <typename T> void operator()(MenuT<T>& m) const {
            std::cout << "-----------\n";

            for (auto& o : m.options) {
                std::cout << "option '" << o.name << "':\t" << o.item << "\n";
                if (o.submenu)
                    (*this)(*o.submenu);
            }
        }
    };
}; 

static const visitors::simple demo { };

int main()
{
    Menu menu = MenuT<int> { 
        { "one",   1 },
        { "two",   2 },
        { "three", 3 },

        { "forty-two", 42, 
                Menu(MenuT<std::string> {
                    {"Life, The Universe And Everything", "LtUae"},
                    {"Dent", "Arthur", 
                        Menu(MenuT<bool> {
                            {"yes", true},
                            {"no", false},
                        })
                    },
                })
        },
    };

    std::cout << std::boolalpha;
    demo(menu);
}
#包括
#包括
#包括
#包括
#包括
模板结构菜单;
使用Menu=boost::使_递归_变量<
boost::递归_包装器,
boost::递归_包装器,
递归包装器
>::类型;
模板结构菜单{
结构选项{
std::字符串名;
T项;
boost::可选子菜单;
选项(std::string name,T,boost::可选子菜单=boost::none)
:名称(名称)、项目(t)、子菜单(子菜单)
{ }
T选择()常量;
无效打印(int n)常量;
};
私人:
模板友元结构菜单;
朋友结构访客;
std::向量选项;
//boost::可选父项;//TODO,例如链接\父项\访问者
枚举选择类型{Normal,Remove};
公众:
枚举特殊位置:大小\u t{u菜单的结束\u=size\u t(-1)};
MenuT()=默认值;
菜单(std::初始值设定项\列表选项):选项(选项){}
void insert(常量std::string&itemName、常量T&T、boost::可选子菜单=boost::none、size\T pos=菜单的结束){
自动it=(位置==菜单的结束)
?选项。结束()
:std::next(options.begin(),std::min(pos,options.size());
选项.emplace(it、itemName、t、子菜单);
}
T choose()const{return chosenItem().first;}
int print()常量;
私人:
std::pair chosenItem(ChoosingType=Normal)const;
};
结构访问者{
结构简单:boost::static\u visitor
{
void操作符()(Menu&m)const{boost::apply_visitor(*this,m);}
模板无效运算符()(菜单(&m)常量{

STD::CUT应该考虑使用继承,并有一个基类用于<代码>菜单<代码>,例如“代码> BaseMeNu,然后子菜单可以指向这个基类……您推荐使用Booo::功能?为什么您希望可以有不同类型的子菜单,而主菜单项必须都是相同的类型?这似乎是一组奇怪的限制,真的。子菜单中的选项也都是相同的类型,但该类型可能与主菜单的选项类型不同。第一个动机来自“返回到上一个菜单”选项,我已经为
菜单
设置了该选项。现在,每当我想使用更改
T
类型(当前必须是新菜单而不是子菜单)前进到另一个菜单时,我不能“返回”“添加到以前不同类型的菜单。将所有菜单作为根菜单中的子菜单放在一起可以解决此问题。这是一个很好的建议!我遵循了您的想法,所有内容似乎都很好,包括print(),但最重要的菜单函数T choose()const除外,它返回从菜单中选择的项目(可能是
template<typename T>
void
print_menu(Menu<T> const& menu)
{
  for(auto i : menu.items) {
    std::cout << i << std::endl;
  }
}

template<typename... T>
void
print_menu(Menu<T...> const& menu)
{
  for(auto i : menu.items) {
    std::cout << i << std::endl;
  }
  print_menu(menu.submenu);
}

int
main(int, char*[])
{
  Menu<int, std::string> menu{};
  menu.items.emplace_back(1);
  menu.submenu.items.emplace_back("42");
  print_menu(menu);
  ...
}
struct ChooseVisitor
{
  void operator()(std::string const& string_val) const
    { /* Do something when a string value is chosen */ }

  void operator()(int int_val) const
    { /* Do something when an int value is chosen */ }
};
template<typename... Types, typename Visitor>
void
choose_menu(Menu<Types...> const& menu, Visitor const& visitor)
{
  for(auto i : menu.items) {
    if(/* is menu item chosen? */) {
      visitor(i);
      return;
    }
  }
  choose_menu(menu.Submenu, visitor);
}

template<typename Type, typename Visitor>
void
choose_menu(Menu<Type> const& menu, Visitor const& visitor)
{
  for(auto i : menu.items) {
    if(/* is menu item chosen? */) {
      visitor(i);
      return;
    }
  }
}
Menu<int, std::string> menu{};
...
choose_menu(menu, ChooseVisitor{});
Menu menu = MenuT<int> { 
    { "one",   1 },
    { "two",   2 },
    { "three", 3 },

    { "forty-two", 42, 
            Menu(MenuT<std::string> {
                {"Life, The Universe And Everything", "LtUae"},
                {"Dent", "Arthur", 
                    Menu(MenuT<bool> {
                        {"yes", true},
                        {"no", false},
                    })
                },
            })
    },
};
struct simple : boost::static_visitor<>
{
    void operator()(Menu& m) const { boost::apply_visitor(*this, m); }

    template <typename T> void operator()(MenuT<T>& m) const {
        std::cout << "-----------\n";

        for (auto& o : m.options) {
            std::cout << "option '" << o.name << "':\t" << o.item << "\n";
            if (o.submenu)
                (*this)(*o.submenu);
        }
    }
};
-----------
option 'one':   1
option 'two':   2
option 'three': 3
option 'forty-two': 42
-----------
option 'Life, The Universe And Everything': LtUae
option 'Dent':  Arthur
-----------
option 'yes':   true
option 'no':    false
#include <string>
#include <vector>
#include <boost/optional.hpp>
#include <boost/variant.hpp>
#include <iostream>

template <typename> struct MenuT;

using Menu = boost::make_recursive_variant < 
        boost::recursive_wrapper<MenuT<int>>,
        boost::recursive_wrapper<MenuT<std::string>>,
        boost::recursive_wrapper<MenuT<bool>>
    >::type;

template <typename T> struct MenuT {
    struct Option {
        std::string           name;
        T                     item;
        boost::optional<Menu> submenu;

        Option(std::string name, T t, boost::optional<Menu> submenu = boost::none)
            : name(name), item(t), submenu(submenu)
        { }

        T choose() const;
        void print(int n) const;
    };

  private:
    template <typename U> friend struct MenuT;
    friend struct visitors;

    std::vector<Option> options;
    //boost::optional<Menu&> parent; // TODO e.g. link_parents_visitor
    enum ChoosingType { Normal, Remove };

  public:
    enum SpecialPosition : size_t { END_OF_MENU = size_t(-1) };

    MenuT() = default;
    MenuT(std::initializer_list<Option> options) : options(options) {}

    void insert(const std::string &itemName, const T &t, boost::optional<Menu> submenu = boost::none, size_t pos = END_OF_MENU) {
        auto it = (pos == END_OF_MENU 
                ? options.end() 
                : std::next(options.begin(), std::min(pos, options.size())));

        options.emplace(it, itemName, t, submenu);
    }

    T choose() const { return chosenItem().first; }
    int print() const;

  private:
    std::pair<T, int> chosenItem(ChoosingType = Normal) const;
};

struct visitors {
    struct simple : boost::static_visitor<>
    {
        void operator()(Menu& m) const { boost::apply_visitor(*this, m); }

        template <typename T> void operator()(MenuT<T>& m) const {
            std::cout << "-----------\n";

            for (auto& o : m.options) {
                std::cout << "option '" << o.name << "':\t" << o.item << "\n";
                if (o.submenu)
                    (*this)(*o.submenu);
            }
        }
    };
}; 

static const visitors::simple demo { };

int main()
{
    Menu menu = MenuT<int> { 
        { "one",   1 },
        { "two",   2 },
        { "three", 3 },

        { "forty-two", 42, 
                Menu(MenuT<std::string> {
                    {"Life, The Universe And Everything", "LtUae"},
                    {"Dent", "Arthur", 
                        Menu(MenuT<bool> {
                            {"yes", true},
                            {"no", false},
                        })
                    },
                })
        },
    };

    std::cout << std::boolalpha;
    demo(menu);
}
boost::optional<Menu&> parent; // TODO e.g. link_parents_visitor