Как развернуть пакет параметров справа налево - PullRequest
13 голосов
/ 19 апреля 2019

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

template <typename... T>
class Foo;

template <typename T>
class Foo<T> {/* base case implementation*/};

template <typename T, typename R, typename... Rs>
class Foo<T, Rs..., R> {
   private:
     Foo<T, Rs...> foo_;
}

К сожалению, вышесказанное достает меня:

class template partial specialization contains template parameters that cannot be deduced;
this partial specialization will never be used

Это кажется мне странным, я бы предположил, что даже если аргументы изменили порядок, Foo<T, Rs..., R> все равно должен соответствовать специализации шаблона.

Я посмотрел на несколько похожих вопросов:

В частности, Частичная специализация шаблона C ++: Почему я не могу сопоставить последний тип в variadic-template?

Однако, ответ с наибольшим количеством голосов (не принят) не имеет смысла для меня. Конечно, я понимаю, что объявление пакета параметров шаблона должно быть последним в объявлении, но я делаю это для специализации шаблона.

Я не уверен, почему компилятор не может сопоставить Foo<T, Rs..., R> с исходным объявлением шаблона Foo<T...> и применяет там порядок объявления пакета параметров.

Другие ответы в этом потоке предлагают, как извлечь последнее значение, но это по-прежнему не позволяет вам выполнить рекурсивную развертку пакета параметров, что в этом вся суть. Разве невозможно развернуть пакет параметров справа налево?

Ответы [ 3 ]

6 голосов
/ 19 апреля 2019

Вот утилита для установки шаблона с обратным порядком параметров шаблона:

#include <type_traits>
#include <tuple>

template <template <typename...> typename Template, typename ...Arg>
struct RevertHelper;

template <template <typename > typename Template, typename Arg>
struct RevertHelper<Template, Arg>
{
    using Result = Template<Arg>;
};

template <template <typename... > typename Template, typename Head, typename ...Tail>
struct RevertHelper<Template, Head, Tail...>
{
private:
    template <typename ...XArgs>
    using BindToTail = Template<XArgs..., Head>;

public:

    using Result = typename RevertHelper<BindToTail, Tail...>::Result;
};

static_assert(std::is_same_v<typename RevertHelper<std::tuple, int, double>::Result, std::tuple<double, int>>, "");

Так что, если вам нужно создать экземпляр Foo с пакетом шаблонов Args... в обратном порядке, вы можете использовать

typename RevertHelper<Foo, Args...>::Result

Чтобы выполнить расширение пакета параметров так, как вам нужно, отправьте в обратную реализацию:

namespace internal {
  template <typename... T>
  class FooHelper;
  template <typename T>
  class FooHelper<T> {/* base implementation */}
  template <typename L, typename R, typename... Rs>
  class FooHelper<T> {
    private:
      Foo<T, Rs...> foo_helper_;
  };
}
template <typename... T>
class Foo {
  typename RevertHelper<internal::FooHelper, T...>::Result foo_helper_;
};
4 голосов
/ 19 апреля 2019

Я не уверен, почему компилятор не может сопоставить Foo<T, Rs..., R> с исходным объявлением шаблона Foo<T...> и применяет там порядок объявления пакета параметров.

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

template <class A, class... B, class C> void foo(A a, B... b, C c);
foo(1, 2, 3, 4); // b is deduced as [2, 3]

Достаточно просто, верно? Теперь, что если C имеет аргумент по умолчанию? Что это делает:

template <class A, class... B, class C=int> void foo(A a, B... b, C c=5);
foo(1, 2, 3, 4);

Существует две интерпретации этого:

  • b выводится как пакет {2, 3}, а c выводится как 4
  • b выводится как пакет {2, 3, 4}, а c выводится как 5

Что предназначено? Или мы просто запрещаем аргументы по умолчанию после пакета параметров функции?

<ч />

К сожалению, у нас нет красивого механизма индексации пакетов. А пока просто используйте Boost.Mp11 :

template <typename... T>
class Foo;

template <typename T>
class Foo<T> {/* base case implementation*/};

template <typename T, typename... Rs>
class Foo<T, Rs...> {
private:
     using R = mp_back<Foo>;
     mp_pop_back<Foo> foo_;
};
3 голосов
/ 19 апреля 2019

Сопоставление шаблонов в шаблонах шаблонов C ++ специально упрощено для простоты алгоритма и понимания.

Взгляните на гипотетический алгоритм, если это возможно:

  1. Получите некоторую декларацию: используя X = Foo<int, char, bool, double>;
  2. Компилятор проверяет специализации: первая - Foo - она ​​отброшена.
  3. Компилятор проверяет специализации: вторая ваша Foo<T, Rs..., R>
    1. T - это int, все в порядке.
    2. R может быть emtpy, давайте попробуем пропустить это.
    3. R - это char, но мы находимся в конце параметров специализации, вернемся к 2.
    4. R - это символ
    5. R - это bool, но мы находимся в конце параметров специализации, вернемся к 2.
    6. R это char, bool
    7. R - это double, все в порядке, выберите этот

Но это только один сценарий: другой - съесть все параметры до конца и обрезать один за другим, чтобы попытаться сопоставить его. Это может быть проблематично, потому что такая специализация шаблонов была бы по сути своей неоднозначной с другой возможной специализацией, которая не кажется здесь двусмысленностью:

template<typename T, typename S>
class Foo<T, S> {};
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...