Проблема с неверным использованием неполного типа при использовании std::tuple_element

Следующий код реализует хеш-функцию для std::tuple который затем используется в другом сегменте моей кодовой базы в std::unordered_map из std::tuples.

// compute hash function recursively through each std::tuple element
template<class Tuple, std::size_t N>
struct tuple_hash_compute {
    static std::size_t hash_compute(const Tuple& t) {
        using type = typename std::tuple_element<N-1, decltype(t)>::type; // OFFENDING LINE
        return tuple_hash_compute<Tuple, N-1>::hash_compute(t)
            + std::hash<type>()(std::get<N-1>(t));
    }
};
// base helper
template<class Tuple>
struct tuple_hash_compute<Tuple, 1> {
    static std::size_t hash_compute(const Tuple& t) {
        using type = typename std::tuple_element<0, decltype(t)>::type; // OFFENDING LINE
        return 51U + std::hash<type>()(std::get<0>(t))*51U;
    }
};
// tuple_hash function object
struct tuple_hash {
    template<class... Args>
    std::size_t operator()(const std::tuple<Args...>& t) const {
        return tuple_hash_compute<decltype(t), sizeof...(Args)>::hash_compute(t);
    } 
    // will use std::unordered_map of std::pair too, so overload reqd
    template<class Ty1, class Ty2>
    std::size_t operator()(const std::pair<Ty1, Ty2>& p) const {
        return tuple_hash_compute<decltype(t), 2>::hash_compute(p);
    }
};

Затем, в качестве примера, я бы использовал этот объект-хэш-функцию следующим образом:

std::unordered_map<std::tuple<int,int,int>, std::size_t, tuple_hash> agg_map;
agg_map.insert(std::make_pair(std::make_tuple(1,2,3), 0U));
agg_map.insert(std::make_pair(std::make_tuple(4,5,6), 1U));

Однако в обоих GCC 6.1.0 а также MSVC2015Я получаю следующие ошибки (обе одинаковые для каждой оскорбительной строки выше):

ошибка: недопустимое использование неполного типа 'class std::tuple_element<2ul, const std::tuple<int,int,int>&>'

Я не совсем уверен, что вызывает эту ошибку (хотя это может быть из-за "абстракции" передачи std::tuple через параметр шаблона Tuple) или как это можно решить, так что любая помощь приветствуется.

1 ответ

Решение

Для параметра, объявленного как показано ниже:

const Tuple& t

decltype(t) выходы:

const Tuple&

Точно так же для параметра, объявленного как:

const std::pair<Ty1, Ty2>& t

decltype(t) выходы:

const std::pair<Ty1, Ty2>&

В обоих случаях созданный тип является ссылкой на кортежный тип. Тем не мение, std::tuple_element не специализируется на ссылках, что означает, что компилятор возвращается к первичному, неопределенному шаблону класса:

template <size_t I, typename T> class tuple_element;

То, что вы хотите, это просто Tuple в первом случае и std::pair<Ty1, Ty2> в последнем случае.

Если у вас есть эта проблема с типом Tuple который может быть или не быть ссылкой, вы можете использовать std::remove_reference вот так:

typename std::tuple_element<num, typename std::remove_reference<Tuple>::type>::type

или, для C++17,

std::tuple_element_t<num, std::remove_reference_t<Tuple>>

PS: не думаю, что это работает с std::reference_wrapper хотя...

Другие вопросы по тегам