Суммирование элементов в std :: integer_sequence - PullRequest
0 голосов
/ 19 марта 2019

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

Шаг 1: Создан набор операций add () - для правильной обработки однородных целочисленных пакетов параметров.Протестировано с add<0,1,2,3>() -call и сработало.

Шаг 2: На этом шаге я хотел передать std::integer_sequence в качестве параметра-шаблона, чтобы суммировать целые числа в последовательности.Не удалось, см. Закомментированный код.

Как передать std::integer_sequence в качестве параметра шаблона?

Одно сообщение об ошибке состоит в том, что ни один экземпляр общего числа шаблона функции не соответствует вызову.Зачем?Я ожидал, что целочисленная последовательность будет эквивалентна пакету параметров int.

В другом сообщении об ошибке говорится, что параметр шаблона Args не является константой.Как это может быть?Я ожидаю, что это будет последующей ошибкой, но я часто видел эту комбинацию ошибок в своих экспериментах.

Я использую компилятор Microsoft VC ++ 2017.

#include <iostream>
#include <utility>

template <typename T>
T add(T first) {
    return first;
}

template <typename T, typename ... Args>
T add(T car, Args... cdr) {
    return car + add(cdr...);
}

template <int ... Args>
int total() {
    return add(Args...);
}


int main(int argc, char** argv)
{
    using std::cout;
    using std::endl;

    int s1 = 
        total<0, 1, 2, 3>();
    std::cout << "s1 = " << s1 << std::endl;

    // The following does not compile:
#if 0
    int s2 = 
        total<std::make_integer_sequence<int, 4>>();
    std::cout << "s2 = " << s2 << std::endl;

    static_assert(s1 == s2, "no match");
#endif

    // This should work:
    //
    {
        cout << typeid(std::make_integer_sequence<int, 4>()).name() << endl;;
        cout << std::make_integer_sequence<int, 4>().size << endl;;
    }

    return 0;
}

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

#include <iostream>
#include <utility>

template <typename T>
constexpr T add(T first) {
    return first;
}

template <typename T, typename ... Args>
constexpr T add(T car, Args... cdr) {
    return car + add(cdr...);
}

template <int ... Args>
constexpr int total() {
    return add(Args...);
}

template<int... Args>
constexpr int total(std::integer_sequence<int, Args...>) {
    return total<Args...>();
}

int main(int argc, char** argv)
{
    using std::cout;
    using std::endl;

    constexpr int s1 =
        total<0, 1, 2, 3>();
    std::cout << "s1 = " << s1 << std::endl;

    // The following now compiles. Oh, happy day!
#if 1
    constexpr int s2 =
        total(std::make_integer_sequence<int, 4> {});

    std::cout << "s2 = " << s2 << std::endl;

    static_assert(s1 == s2, "no match");
#endif

    // This should work:
    //
    {
        cout << typeid(std::make_integer_sequence<int, 4>()).name() << endl;;
        cout << std::make_integer_sequence<int, 4>().size << endl;;
    }

    return 0;
}

Ответы [ 4 ]

4 голосов
/ 19 марта 2019

Это было бы намного проще сделать с выражениями сгиба :

template<class T, T... Args>
constexpr T total(std::integer_sequence<T, Args...> = {}) {
    return (Args + ...);
}

int main() {
    int s2 = 
        total(std::make_integer_sequence<int, 4>{});
    // Or calling directly
    int s1 = total<int, 0, 1, 2, 3>();
}

Обратите внимание на аргумент std::integer_sequence по умолчанию, поэтому, если вы передадите целочисленную последовательность, он выведет параметр Args.

Если вы хотите иметь возможность вызывать его с типом std::integer_sequence в шаблоне, просто сделайте вспомогательную функцию:

template<class IntegerSequence>
constexpr auto total_of(IntegerSequence argument = {}) {
    return total(argument);  // Will deduce `T` and `Args`
}

total_of<std::make_integer_sequence<int, 4>>();
1 голос
/ 19 марта 2019

Я ожидал, что целочисленная последовательность будет эквивалентна пакету параметров int.

Это не так: std::integer_sequence - это тип; так

total<std::make_integer_sequence<int, 4>>();

может соответствовать шаблонной функции total(), которая получает тип

template <typename T>
int total ()
 { return ???; }

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

Как я могу передать std :: integer_sequence в качестве параметра шаблона?

Решение (ну ... возможное решение) - это частичная специализация.

К сожалению, функция не может быть частично специализированной.

Но если total - это struct (или class), все будет иначе.

Вы можете написать total следующим образом

template <typename>
struct total;

template <typename T, T ... Is>
struct total<std::integer_sequence<T, Is...>>
   : public std::integral_constant<T, (Is + ...)>
 { };

, чтобы вы могли использовать его следующим образом

constexpr auto s2 { total<std::make_integer_sequence<int, 4>>::value };

Не по теме: объявите constexpr переменные, которые вы хотите использовать в static_assert() тесте.

Ниже приведен пример полной компиляции C ++ 17 (total struct переименована total2 во избежание коллизии с функцией total())

#include <utility>

template <typename>
struct total2;

template <typename T, T ... Is>
struct total2<std::integer_sequence<T, Is...>>
   : public std::integral_constant<T, (Is + ...)>
 { };

template <typename T>
constexpr T add (T first)
 { return first;}

template <typename T, typename ... Args>
constexpr T add (T car, Args ... cdr)
 { return car + add(cdr...); }

template <int ... Args>
constexpr int total ()
 { return add(Args...); }

int main ()
 {
   constexpr auto s1 { total<0, 1, 2, 3>() };
   constexpr auto s2 { total2<std::make_integer_sequence<int, 4>>::value };

   static_assert(s1 == s2);
 }
0 голосов
/ 19 марта 2019

Я ожидал, что целочисленная последовательность будет эквивалентна пакету параметров int.

Но вы можете увидеть это не так, написав два расширения:

template <int ... Args> int total();

первый случай

total<0, 1, 2, 3>();
// => Args... is (0, 1, 2, 3)

, который является допустимым целочисленным пакетом, а второй -

total<std::make_integer_sequence<int, 4>>();
// => Args... must be std::integer_sequence<int, 0, 1, 2, 3>

, который явно не похож на целочисленную пачку. Ссылка на документацию для std::integer_sequence для справки.

Если вы хотите превратить эту целочисленную последовательность обратно в исходный пакет параметров, переданный make_integer_sequence, вам нужно использовать перегрузку дедукции аргументов шаблона. Ответ SergeyA показывает:

template<int... Args>
int total(std::integer_sequence<int, Args...> ) {
    return add(Args...);
}

(параметр функции является фиктивной, просто чтобы можно было вывести Args...).

Выражение сгиба в любом случае намного лучше, чем рекурсия старого стиля, если вы можете его использовать.

0 голосов
/ 19 марта 2019

Один из способов сделать это - добавить перегрузку для total и вызвать ее по-другому:

template<int... Args>
int total(std::integer_sequence<int, Args...> ) {
    return add(Args...);
}

и чем звонить

nt s2 = 
    total(std::make_integer_sequence<int, 4>>{});

Это решит проблему вызова. Чтобы использовать результат функции в static_assert, вам также необходимо убедиться, что он (и переменная, хранящая его результат) помечены как constexpr.

...