Прежде всего имейте в виду, что вы не можете делать то, что хотите напрямую. C++ - строго типизированный язык, поэтому тип результата функции должен быть известен в время компиляции. Поэтому, конечно, если строка, которую вы передаете получателю, известна во время выполнения, вы не можете отправлять функцию во время компиляции, чтобы компилятор выводил соответствующий тип результата. Но когда вы соглашаетесь с тем, что вам нужно стирать стирание, чтобы стереть тип результата получения, вы могли бы использовать, например, boost::variant
, чтобы решить вашу проблему. C++ 14 Примера (используя импульс, так как C++ 17 варианта должна быть доступны в станде):
#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]
Я уверен, что вы будете в состоянии оптимизировать код (например, сделать использование семантики перемещения/безупречной переадресации и т. д.). Это только для того, чтобы представить вам, как начать свою реализацию.
Не могли бы вы показать желаемое использование функции 'get_value'? В частности, ключи ключей будут известны во время компиляции или не будут известны до запуска программы? –
Строка не будет известна до запуска программы. Функция 'get' будет частью API библиотеки C++. Имена передаются как 'string', так как это будет более удобным для программиста, чем использование индексов. –
Каким будет обходной путь, когда строка известна во время компиляции? –