Почему рекурсивное значение шаблона constexpr не компилируется? - PullRequest
0 голосов
/ 23 июня 2018

Я определяю способ узнать положение типа в списке типов, используя рекурсивные шаблоны в C ++ 17. Я пробовал два способа: один с использованием значения constexpr и один с использованием функции constexpr. Второй, используя оператор if, компилируется, в то время как первый, используя тернарный оператор, не компилируется.

#include <type_traits>
#include <iostream>

template<typename Searching, typename First, typename...Others>
constexpr std::size_t index_of = std::is_same_v<Searching,First> ? 0 : 1 + index_of<Searching,Others...>;

template<typename Searching, typename First, typename...Others>
constexpr std::size_t IndexOf() {
    if constexpr(std::is_same_v<Searching,First>)
        return 0;
    else return 1 + IndexOf<Searching,Others...>();
};

int main() {
    std::cout << index_of<int, int> << std::endl; //does not compile
    std::cout << IndexOf<int, int>() << std::endl; //compile
    //both should return 0
    return 0;
}

Мой компилятор, migw64, говорит:

неверное количество аргументов шаблона (1, должно быть не менее 2)
constexpr std :: size_t index_of = std :: is_same_v <Поиск, первый>? 0: 1 + index_of <Поиск, другие ...>;

Из того, что я понимаю, троичный оператор должен вычислять два своих операнда, поэтому его нельзя использовать в рекурсии этого типа.

Я прав? и если да, то почему это так?
Спасибо.

Ответы [ 2 ]

0 голосов
/ 24 июня 2018

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

Не оценка, это реализация. Когда создается выражение std::is_same_v<Searching, First> ? 0 : 1 + index_of<Searching, Others...>, должны быть созданы все три операнда (не оценены), поэтому возникает ошибка из-за создания экземпляра index_of<Searching, Others...>. Это аналогично разнице между if и if constexpr. Если вы измените if constexpr другим способом на if, он тоже не скомпилируется.

Обходной путь - использовать ваш второй способ (то есть шаблон функции) для инициализации index_of, например

template<typename Searching, typename First, typename... Others>
constexpr std::size_t IndexOf() {
    if constexpr(std::is_same_v<Searching,First>)
        return 0;
    else return 1 + IndexOf<Searching,Others...>();
};

template<typename Searching, typename First, typename...Others>
constexpr std::size_t index_of = IndexOf<Searching, First, Others...>();

или используйте шаблон специализации:

template<typename Searching, typename First, typename... Others>
constexpr std::size_t index_of = 1 + index_of<Searching, Others...>;

template<typename Searching, typename... Others>
constexpr std::size_t index_of<Searching, Searching, Others...> = 0;

Если вам нужно более четкое сообщение об ошибке, вы можете заключить шаблон переменной в класс и использовать static_assert.

0 голосов
/ 24 июня 2018

Я собираюсь начать с конца вопроса, а затем продолжить.

Из того, что я понимаю, троичному оператору нужно вычислить два его операнда

Нет.Тернарный (что означает «сделан из трех») оператор имеет три операнда, а не два.При оценке этого оператора оцениваются два из трех операндов: условие и любой операнд, который выберет условие.

Оценка не в том, в чем заключается ваша проблема.

первый, используя троичныйоператор, не компилируется.

Я думаю, я понимаю, почему это так.Вы присваиваете результат условного оператора для std::size_t.Для того, чтобы это скомпилировалось, тип этого результата должен быть std::size_t или быть конвертируемым в этот тип.Таким образом, компилятор должен определить тип результата.Я нашел правила определения типа .Первое правило применяется, если второй или третий операнды имеют тип void.Таким образом, даже если один из этих операндов не будет оценен, оба их типа должны быть известны.

ОК, так каков тип вашего третьего операнда, который не будетоценивать?Ну, это 1 + index_of<int>, поэтому нам лучше проверить объявление index_of<int>.Нам нужны два параметра.Сообщение об ошибке Cue.

Это, вероятно, что-то, с чем вам придется иметь дело в любом случае, так как вы должны получить одинаковую ошибку для любого подхода в случае "not found" (например: index_of<unsigned, int, long, float>).Как вы, возможно, заметили, сообщение об ошибке по умолчанию не дает хороших результатов, описывая, что пошло не так, поэтому, вероятно, хорошей идеей для вашего шаблона является конкретное рассмотрение этого случая, даже если обращение к этому случаю просто означает предоставление более понятной ошибки компилятора.

...