Выберите имя функции на основе параметра шаблона

Есть ли способ автоматически выбирать между несколькими нешаблонными функциями на основе параметра шаблона?

Пример:

class Aggregate
{
public:
     std::string asString();
     uint32_t asInt();
private:
     // some conglomerate data
};

template <typename T>
T get(Aggregate& aggregate)
{
     // possible map between types and functions?
     return bind(aggregate, typeConvert[T])(); ??
     // or
     return aggregate.APPROPRIATE_TYPE_CONVERSION();
}

Решение было бы неплохо выдать ошибку компилятора, если нет доступного хорошего преобразования, т.е.

get<double>(aggregate); // compile error

Я не хочу использовать специализацию шаблона, т.е.

template<>
int get(Aggregate& aggregate)
{
    return aggregate.asInt();
}

потому что это приводит к дублированию кода, когда ваша функция get() имеет более одной строки кода


person Sam    schedule 02.04.2014    source источник


Ответы (2)


Вы можете сделать что-то вроде (требуется C++11): (https://ideone.com/UXrQFm)

template <typename T, typename... Ts> struct get_index;

template <typename T, typename... Ts>
struct get_index<T, T, Ts...> : std::integral_constant<std::size_t, 0> {};

template <typename T, typename Tail, typename... Ts>
struct get_index<T, Tail, Ts...> :
    std::integral_constant<std::size_t, 1 + get_index<T, Ts...>::value> {};

template <typename T, typename Tuple> struct get_index_in_tuple;

template <typename T, typename ... Ts>
struct get_index_in_tuple<T, std::tuple<Ts...>> : get_index<T, Ts...> {};


class Aggregate
{
public:
     std::string asString();
     uint32_t asInt();
private:
     // some conglomerate data
};

template <typename T>
T get(Aggregate& aggregate)
{
    using types = std::tuple<uint32_t, std::string>;
    auto funcs = std::make_tuple(&Aggregate::asInt, &Aggregate::asString);

    return (aggregate.* (std::get<get_index_in_tuple<T, types>::value>(funcs)))();
}
person Jarod42    schedule 02.04.2014
comment
Есть ли способ заставить его работать на С++ 03? Не у всех есть компилятор C++11 - person Sam; 03.04.2014
comment
@sammy: вместо вариативного шаблона и std::tuple вы можете использовать typelist, но это было бы намного более многословно. - person Jarod42; 03.04.2014

Пеший путь заключается в том, чтобы определить каждый возможный вариант отдельно:

template <typename T> T get(Aggregate &);    // leave undefined

template <> uint32_t get(Aggregate & a) { return a.asInt(); }

// ...

В отсутствие какой-либо более систематической структуры, которая кодирует, какая функция служит какому преобразованию, я думаю, что это лучшее, что вы можете сделать. Однако, возможно, стоит переопределить Aggregate, чтобы сделать его более интроспективным.

person Kerrek SB    schedule 02.04.2014
comment
Я знаю, я пытаюсь реорганизовать некоторый код, который использует тот же самый подход, так как это приводит к дублированию кода в get() - у меня есть более одного вызова функции - person Sam; 02.04.2014
comment
Можно ли не заворачивать повторяющийся код и вызывать обертку из своей специализации? - person Bart van Nierop; 02.04.2014