我尝试实现一个包含多个名称 - 值对的数据结构,其值可能在类型上有所不同:
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}的输入类型必须是一个字符串。
有人有想法吗?
答案 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;
}
我确定您能够优化代码(例如,使用移动语义/完美转发等)。这只是为了向您介绍如何开始实施。