C ++ Variadic функции без аргументов - PullRequest
4 голосов
/ 04 июня 2019

У меня есть несколько классов (Foo и Bar здесь для простоты)

struct Bar {};
struct Foo {};

и функция, которая принимает один параметр шаблона и делает что-то на основе этого типа:

template <typename T>
constexpr void doSomething() { cout << "Am I a Foo? " << is_same<T,Foo>::value << endl; }

В моем коде мне дан пакет параметров шаблона Foo с и Bar с, и я должен вызывать функцию doSomething() для каждого из них (меня не волнует порядокв котором выполняются функции).

doStuff<Foo, Bar, Bar>(); // --> True / False / False

Пока что единственное решение, которое я мог придумать, это:

template <typename... Ts>
class Doer;

template <>
struct Doer <> {
    static constexpr void doStuff() {}
};

template <typename Head, typename... Tail>
struct Doer <Head, Tail...> {
    static constexpr void doStuff() {
        doSomething<Head>();
        Doer<Tail...>::doStuff();
    }
};

template <typename... Ts>
constexpr void doStuff() {
    return Doer<Ts...>::doStuff();
}

doStuff<Foo, Bar, Bar>(); // --> True / False / False

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

constexpr void doStuff() { }

template <typename Head, typename... Tail>
constexpr void doStuff() {
    doSomething<Head>();
    doStuff<Tail...>();   // --> Compile Error
}

, но компилятор не работает, потому что он не может понять, что doStuff<>() на самом деле doStuff().Если у меня есть аргументы в моих функциях variadic, то компилятор достаточно умен, чтобы разрешить этот конфликт, поскольку он применяет вывод типа шаблона:

constexpr void doStuff() { }

template <typename Head, typename... Tail>
constexpr void doStuff(Head arg, Tail... args) {
    doSomething<Head>();
    doStuff(args...);
}

Foo f1;
Bar b1, b2;
doStuff<Foo, Bar, Bar>(f1, b1, b2); // --> True / False / False

Я что-то упустил?Есть ли способ заставить мою функцию с переменным значением работать без использования параметров функции или шаблонов классов?

1 Ответ

6 голосов
/ 04 июня 2019

но компилятор не работает, потому что не может понять, что doStuff <> () на самом деле является doStuff ().

Как насчет

template <int = 0>
constexpr void doStuff() { }

template <typename Head, typename... Tail>
constexpr void doStuff() {
    doSomething<Head>();
    doStuff<Tail...>();
}

Я имею в виду: если проблема в том, что в конце список вариантов шаблона пуст, преобразуйте базовый регистр в версию шаблона с параметром шаблона (совершенно другой: целое число вместо типов) со значением по умолчанию.

Итак, когда Tail... пусто, вызов doStuff<Tail...>(), то есть doStuff<>(), соответствует doStuff<0>() (учитывая значение по умолчанию в первой функции), поэтому вызовите наземный регистр.

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

template <typename... Ts>
constexpr void doStuff() {
    (doSomething<Ts>(), ...);
}

В C ++ 14 вы можете смоделировать сворачивание шаблона следующим образом

template <typename... Ts>
constexpr void doStuff() {
    using unused = int[];

    (void) unused { 0, ((void)doSomething<Ts>(), 0)... };
}

Предыдущее решение работает также с C ++ 11, но не как constexpr (но doSomething() не может быть constexpr в C ++ 11).

С учетом того, что вас не волнует порядок выполнения функций, я предлагаю решение C ++ 11, поддерживающее constexpr, и оно основано на расширении пакета шаблонов при вызове поддельной функции (или может быть, не подделка ... увидимся).

Но для этого необходимо, чтобы doSomething() было constexpr (поэтому в C ++ 11 не может быть void), а также doStuff() не может быть void

#include <iostream>

template <typename T> 
constexpr std::size_t doSomething ()
 { return sizeof(T); }

template <typename ... Ts>
constexpr int fakeFunc (Ts const & ...)
 { return 0; }

template <typename ... Ts>
constexpr int doStuff ()
 { return fakeFunc( doSomething<Ts>()... ); }


int main()
 {
   constexpr int a { doStuff<char, short, int, long, long long>() };

   (void)a;
 }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...