获取具有泛型返回类型的函数

时间:2016-12-17 11:03:20

标签: c++ templates tuples c++14

我尝试实现一个包含多个名称 - 值对的数据结构,其值可能在类型上有所不同:

template< typename T >
struct name_value_pair
{
  std::string name;
  T           value;
};


template< typename... Ts >
class tuple_of_name_value_pairs
{
  public:
    /* type of value */ get_value( std::string n )
    {
      // return the value that the element in
      // _name_value_pairs with name "n" comprises
    }

  private:
    std::tuple<Ts...> _name_value_pairs:
};

不幸的是,我不知道如何实现get函数。

解决方法是将名称命名为integer而不是string,并根据std::get使用实现,但这里没有选项:{{1}的输入类型必须是一个字符串。

有人有想法吗?

1 个答案:

答案 0 :(得分:2)

首先要记住,你不能直接做你想做的事。 C ++是一种强类型语言,因此在编译时中必须知道必须的函数结果类型。因此,如果传递给getter的字符串在运行时是已知的,那么您无法在编译时调度函数以让编译器推导出适当的结果类型。但是当你接受需要类型擦除来擦除getter结果类型时,你可以使用例如boost::variant来处理您的问题。 C ++ 14示例(使用boost,因为c ++ 17变体应该在std中可用):

#include <boost/variant.hpp>
#include <utility>
#include <iostream>
#include <tuple>

template< typename T >
struct name_value_pair
{
  using type = T;
  std::string name;
  T           value;
};

template <std::size_t N, class = std::make_index_sequence<N>>
struct getter;

template <std::size_t N, std::size_t... Is>
struct getter<N, std::index_sequence<Is...>> {
     template <class Val, class Res>
     void setRes(Val &val, Res &res, std::string &s) {
         if (val.name == s)
             res = val.value;
     }

     template <class Tup>
     auto operator()(Tup &tuple_vals, std::string &s) {
         boost::variant<typename std::tuple_element<Is, Tup>::type::type...> result;
         int helper[] = { (setRes(std::get<Is>(tuple_vals), result, s), 1)... };
         (void)helper;
         return result;
     }
};

template <std::size_t N, class = std::make_index_sequence<N>>
struct setter;

template <std::size_t N, std::size_t... Is>
struct setter<N, std::index_sequence<Is...>> {
     template <class Val, class SVal>
     std::enable_if_t<!std::is_same<SVal, typename Val::type>::value> setVal(Val &, std::string &, const SVal &) { }
     template <class Val>
     void setVal(Val &val, std::string &s, const typename Val::type &sval) { 
         if (val.name == s)
             val.value = sval;
     }

     template <class Tup, class Val>
     auto operator()(Tup &tuple_vals, std::string &s, const Val &val) {
         int helper[] = { (setVal(std::get<Is>(tuple_vals), s, val), 1)... };
         (void)helper;
     }
};

template <class T, class Res>
using typer = Res;

template< typename... Ts >
class tuple_of_name_value_pairs
{
  public:
    auto get_value( std::string n )
    {
       return getter<sizeof...(Ts)>{}(_name_value_pairs, n);
    }
    template <class T>
    void set_value( std::string n, const T& value) {
        setter<sizeof...(Ts)>{}(_name_value_pairs, n , value);
    }
    void set_names(typer<Ts, std::string>... names) {
        _name_value_pairs = std::make_tuple(name_value_pair<Ts>{names, Ts{}}...);
    }

  private:
    std::tuple<name_value_pair<Ts>...> _name_value_pairs;
};

int main() {
    tuple_of_name_value_pairs<int, float, double> t;
    t.set_names("abc", "def", "ghi");
    t.set_value("abc", 1);
    t.set_value("def", 4.5f);
    t.set_value("ghi", 5.0);

    std::cout << t.get_value("def") << std::endl;
}

[live demo]

我确定您能够优化代码(例如,使用移动语义/完美转发等)。这只是为了向您介绍如何开始实施。