Вложение вложенного аргумента шаблона для шаблонов классов не работает

В этот вопрос& Я написал небольшой класс-обертку, который предоставляет обратный итератор для доступа к диапазону, полагаясь на вывод аргумента шаблона функции языка c ++ 1z для шаблонов классов (p0091r3, p0512r0)

#include <iostream>
#include <iterator>
#include <vector>

template<class Rng>
class Reverse
{
Rng const& rng;
public:
Reverse(Rng const& r) noexcept
:
rng(r)
{}

auto begin() const noexcept { using std::end; return std::make_reverse_iterator(end(rng)); }
auto end()   const noexcept { using std::begin; return std::make_reverse_iterator(begin(rng)); }
};

int main()
{
std::vector<int> my_stack;
my_stack.push_back(1);
my_stack.push_back(2);
my_stack.puhs_back(3);

// prints 3,2,1
for (auto const& elem : Reverse(my_stack)) {
std::cout << elem << ',';
}
}

Тем не менее, делая вложенное приложение Reverse не дает исходный порядок итераций

// still prints 3,2,1 instead of 1,2,3
for (auto const& elem : Reverse(Reverse(my_stack))) {
std::cout << elem << ',';
}

Живой пример (одинаковый вывод для g ++ 7.0 SVN и clang 5.0 SVN)

Похоже, виновником является вывод аргумента шаблона для шаблонов классов, потому что обычная функция-обертка допускает правильное вложение

template<class Rng>
auto MakeReverse(Rng const& rng) { return Reverse<Rng>(rng); }

// prints 1,2,3
for (auto const& elem : MakeReverse(MakeReverse(my_stack))) {
std::cout << elem << ',';
}

Живой пример (один и тот же вывод для g ++ и clang)

Вопрос: вычитание вложенного аргумента шаблона для шаблонов классов должно работать только на «один уровень» или это ошибка в текущих реализациях g ++ и clang?

13

Решение

Ответ Петра правильно объясняет, что происходит — конструктор перемещения лучше соответствует вашему конструктору.

Но (ч / т Агар Т.С. как обычно) есть лучшее решение, чем просто написать фабрику в любом случае: вы можете добавить явное руководство по выводу для обработки упаковки:

template <class R>
Reverse(Reverse<R> ) -> Reverse<Reverse<R>>;

Смысл этого в том, чтобы переопределить кандидата на вычет копирования, благодаря недавно добавленному предпочтению в [over.match.best] для этого:

Учитывая эти определения, жизнеспособная функция F1 определяется как лучшая функция, чем другая жизнеспособная функция F2 если […] F1 генерируется из руководства по удержанию (13.3.1.8) и F2 не является.

Следовательно, мы бы четыре сгенерированные функции, снова заимствуя из имен Петра:

template <typename Rng>
Reverse<Rng> foo(const Rng& r);             // #1

template <typename Rng>
Reverse<Rng> foo(const Reverse<Rng>& r);    // #2

template <typename Rng>
Reverse<Rng> foo(Reverse<Rng>&& r);         // #3

template <typename Rng>
Reverse<Reverse<Rng>> foo(Reverse<Rng> r);  // #4 - same-ish as #2/3, but deduction guide

До, #3 был предпочтен как более специализированный. Сейчас, #4 является предпочтительным как руководство по удержанию. Итак, мы все еще можем написать:

for (auto const& elem : Reverse(Reverse(my_stack))) {
std::cout << elem << ',';
}

и это работает.

5

Другие решения

Это может быть объяснено в [Over.match.class.deduct] / р1:

Набор функций и шаблонов функций состоит из:

  • Для каждого конструктора шаблона класса, обозначенного
    Имя Шаблона, шаблон функции со следующими свойствами:
  • Параметры шаблона — это параметры шаблона класса, за которыми следуют параметры шаблона (включая значения по умолчанию).
    аргументы шаблона) конструктора, если есть.

  • Типы параметров функции — это типы конструктора.

  • Тип возвращаемого значения — это специализация шаблона класса, обозначенная Имя Шаблона и аргументы шаблона, соответствующие шаблону
    параметры, полученные из шаблона класса.

Насколько я понимаю, компилятор изобретает следующие две функции (две — включая конструктор копирования, который неявно генерируется для этого класса):

template <typename Rng>
Reverse<Rng> foo(const Rng& r);           // #1

template <typename Rng>
Reverse<Rng> foo(const Reverse<Rng>& r);  // #2

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

foo(Reverse<std::vector<int>>(my_stack));

который разрешается до # 2, потому что этот является более специализированным. Вывод таков:

Reverse(Reverse(my_stack))

включает в себя конструктор копирования для создания внешнего Reverse пример.

7

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector