Тип списка не хочет компилировать - PullRequest
0 голосов
/ 17 апреля 2020

Я пытаюсь реализовать какой-то список типов. Мне интересно, почему следующий код не компилируется. На мой взгляд, это должно по крайней мере компилировать.

#include <iostream>
#include <type_traits>

struct Non_t {};

template<typename THead, typename TTail = Non_t>
struct Type_list
{
    using Head = THead;
    using Tail = TTail;
};

struct Conv1 {};
struct Conv2 {};
struct Conv3 {};

using Types = 
    Type_list<Conv1
>;

template<typename TTypeList, typename TWanted>
void dispatch()
{
    if constexpr (std::is_same<TTypeList, Non_t>::value)
    {
        std::cout << "Not found :( " << std::endl;

        return; 
    }

    using Head = typename TTypeList::Head;
    using Tail = typename TTypeList::Tail;
    using Wanted = TWanted;

    if constexpr (!std::is_same<Head, Wanted>::value)
    {
        dispatch<Tail, Wanted>();
    }
    else 
    {
       std::cout << "It works" << std::endl;
    }

}

int main()
{
    dispatch<Types, Conv3>();

    return 0;
}

Все работает нормально только тогда, когда список типов содержит нужный мне тип.

Я пробовал либо g cc 9.3, либо лязг 10.

Оба компилятора показывают похожее сообщение об ошибке: 'ошибка: нет типа с именем' Head 'в' struct Non_t ''

Я попытался скомпилировать этот код в https://godbolt.org/ с помощью clang 10, и я вижу следующие подсказки от clang:

примечание: в создании экземпляра шаблона функции специализация dispatch<Type_list<Conv1, Non_t>, Conv3> запрашивается здесь x86-64 clang 10.0.0 # 1

примечание: в экземпляре шаблона функции специализация dispatch<Type_list<Conv1, Non_t>, Conv3> запрашивается здесь x86-64 clang 10.0.0 # 1

Редактировать: он компилируется, только если я помещаю часть функции после первого «оператора if» в другой блок else. Почему это так?

1 Ответ

0 голосов
/ 17 апреля 2020

Потому что, как говорится в ошибке, Non_t не имеет типа с именем Head. Тот факт, что ваш оператор using Head = ... приходит после вашего возврата, не имеет никакого значения.

Давайте рассмотрим пример счетчика.

int main() {
    int i = 2;
    return i;

    i.thisIsNotValid();
}

Здесь мы получим ошибку компиляции. Неважно, что мы никогда не достигнем этой части кода во время выполнения. Компилятор не учитывает это. Он просто проверяет правильность всего написанного кода и генерирует для него машинный код.

С другой стороны, используя блоки if constexpr и else, мы сообщаем компилятору вычислить что-то во время компиляции и используйте только одну из веток. Другая полностью отбрасывается.

template<typename TTypeList, typename TWanted>
void dispatch()
{
    if constexpr (std::is_same<TTypeList, Non_t>::value)
    {
        std::cout << "Not found :( " << std::endl;

        return; 
    } else {

        using Head = typename TTypeList::Head;
        using Tail = typename TTypeList::Tail;
        using Wanted = TWanted;

        if constexpr (!std::is_same<Head, Wanted>::value)
        {
            dispatch<Tail, Wanted>();
        }
        else 
        {
           std::cout << "It works" << std::endl;
        }
    }
}

Теперь нижняя часть функции будет полностью отброшена во время компиляции , если условие истинно.

...