функция шаблона для разных типов - PullRequest
0 голосов
/ 29 апреля 2018

Как мне сделать шаблон функции, который работает хотя бы для любой тип, перечисления, std::vector<T>, std::vector<E> для перечисления E?

Я попробовал следующее с использованием разрешения перегрузки и enable_if, но, хотя оно работает для int и vector<int>, происходит сбой с vector<E>, говоря, что не может найти функцию для вызова:

struct Details
{
    template<typename T>
    typename std::enable_if<std::is_enum<T>::value, T>::type
        get_impl(const std::string& key, T*) const
    {
        return static_cast<T>(get_impl<int>(key, static_cast<int*>(0)));
    }

    template<typename T>
    int get_impl(const std::string& key, int*) const
    {
        return int();
    }

    template<typename T>
    typename std::enable_if<std::is_enum<T>::value, std::vector<T>>::type
        get_impl(const std::string& key, std::vector<T>*) const
    {
        return std::vector<T>();
    }


    template<typename T>
    std::vector<int> get_impl(const std::string& key, std::vector<int>*) const
    {
        return T();
    }

    template<typename T>
    T getValue(const std::string& key)
    {
        return get_impl<T>(key, static_cast<T*>(0));
    }
};

int main()
{
    enum E {A, B};
    Details details;
    details.getValue<int>(""); // ok
    details.getValue<std::vector<int>>(""); // ok
    details.getValue<std::vector<E>>(""); // error: no matching function
}

У меня есть специальная перегрузка для vector<enum>, почему она не работает?

1 Ответ

0 голосов
/ 29 апреля 2018

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

В случае enum вы звоните:

details.getValue<std::vector<E>>("");

Какие звонки:

template<typename T>
T getValue(const std::string& key)
{
    return get_impl<T>(key, static_cast<T*>(0));
}

Здесь T равно std::vector<E>. Все идет нормально. Теперь перегрузка, которую вы хотите вызвать:

template<typename T>
typename std::enable_if<std::is_enum<T>::value, std::vector<T>>::type
    get_impl(const std::string& key, std::vector<T>*) const

Но вы называете это get_impl<T>(...). Это означает, что при попытке разрешить эту перегрузку T равен std::vector<E>, а последний аргумент - std::vector<std::vector<E>>*. Таким образом, этот шаблон функции просто не является кандидатом - последний аргумент не совпадает, и даже если он это сделал, std::vector<E> не является перечислением, поэтому enable_if исключит его.

<ч />

Способ исправить это - сохранить все в мире дедукций. И пока мы там, избегайте использования нулевых указателей для отправки тегов. Это сбивает с толку - потому что теперь некоторые части вашего кода используют указатели в качестве указателей, а некоторые просто используют их как типы. Мы можем просто сделать специальный тип для использования в качестве типа:

template <typename T> struct tag { };

И передать это в:

template <typename T>
T getValue(std::string const& key ) {
    return get_impl(key, tag<T>{});
}

Обратите внимание, что я больше не указываю тип для get_impl, это будет выведено. Теперь ваш случай перечисления:

template<typename T>
typename std::enable_if<std::is_enum<T>::value, std::vector<T>>::type
get_impl(const std::string& key, tag<std::vector<T>>) const

И это будет работать. Мы вывели бы T как E из параметра tag, как вы и хотели, а E - это перечисление, так что это работает.

Обратите внимание, что даже если вы используете C ++ 11, шаблоны псевдонимов работают. Поэтому, пока у вас нет доступа к std::enable_if_t, вы все равно можете написать:

namespace xstd {
    template <bool B, typename T>
    using enable_if_t = typename std::enable_if<B, T>::type;
}

Что облегчает чтение всего этого:

template<typename T>
xstd::enable_if_t<std::is_enum<T>::value, std::vector<T>>
get_impl(const std::string& key, tag<std::vector<T>>) const
...