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

У меня возникают проблемы при написании элементов шаблона не шаблонного класса, особенно при выводе параметров шаблона аргумента шаблона шаблона.

Следующий код, минимальный, иллюстрирует мою проблему. Я подозреваю, что там как минимум две разные проблемы.

#include <iostream>
#include <list>

using namespace std;

struct A
{
    list<int> l;

    template<template<class> class Iterable> 
    A(Iterable<int>& it);
};

template<template<class> class Iterable>
A::A(Iterable<int>& it) : l(list<int>())
{
    for(int i : it)
        l.push_back(i);
}

int main()
{
    list<int> l = {1, 2, 3, 4};
    A a(l);
    for(int i : a.l)
        cout << i << " ";
     cout << endl;

    A b = {1, 2, 3, 4};
    for(int i : b.l)
        cout << i << " ";
    cout << endl;
}

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

1 ответ

Решение

Я подозреваю, что там как минимум две разные проблемы.

Правильно: я вижу три разные проблемы.

1) std::list нужны два параметра шаблона; второй по умолчанию (std::allocator<T>, где T первый параметр шаблона); а также векторам, запросам, наборам и т. д. требуется больше, чем параметр шаблона (некоторые по умолчанию).

Так template<template<class> class> не совпадает std::list и другие контейнеры STL.

Чтобы быть более общим, я предлагаю что-то

template <template <typename...> class Iterable, typename ... Ts> 
A (Iterable<int, Ts...> & it);

2) A b = {1, 2, 3, 4}; не работает, потому что вы вызываете конструктор с четырьмя аргументами, и у вас есть только конструкторы с одним аргументом. Таким образом, вы должны явным образом указать контейнер как

A b = std::list<int>{1, 2, 3, 4};

Учитывая, что вы ожидаете std::initializer_list как контейнер по умолчанию, если вы согласны удвоить графики ({ { 1, 2, 3, 4 } }, вместо { 1, 2, 3, 4 }, чтобы передать один аргумент в конструктор) вы можете указать std::initializer_list (или другой контейнер, если вы предпочитаете) по умолчанию Iterable,

Я имею в виду... если вы объявите конструктор следующим образом

   template <template <typename...> class Iterable = std::initializer_list,
             typename ... Ts> 
   A (Iterable<int, Ts...> const & it);

тогда вы можете инициализировать b как

   A b {{1, 2, 3, 4}};

3) подпись Iterable<int, Ts...> & itв конструкторе шаблона примите ссылку на l-значение, поэтому примите

list<int> l = {1, 2, 3, 4};
A a(l);

но не принимает

A b = std::list<int>{1, 2, 3, 4};

так как std::list<int>{1, 2, 3, 4} это р-значение.

Чтобы решить эту проблему, вы можете написать другой конструктор для ссылок на r-значения, но в этом случае я подозреваю, что приемлемо просто изменить конструкцию шаблона, чтобы она могла принимать константные ссылки на l-значения

A (Iterable<int, Ts...> const & it); 
// .....................^^^^^

Предложение о бонусе (вне темы): если вы заинтересованы в приеме контейнеров STL, вы можете упростить конструктор шаблонов, используя l конструктор, который принимает пару итераторов (begin(), end()), так

template <template <typename...> class Iterable, typename ... Ts>
A::A (Iterable<int, Ts...> const & it) : l{it.cbegin(), it.cend()}
 { }

или также

template <template <typename...> class Iterable, typename ... Ts>
A::A (Iterable<int, Ts...> const & it) : l{std::cbegin(it), std::cend(it)}
 { }

Ниже ваш код изменен

#include <initializer_list>
#include <iostream>
#include <list>

struct A
 {
   std::list<int> l;

   template <template <typename...> class Iterable = std::initializer_list,
             typename ... Ts> 
   A (Iterable<int, Ts...> const & it);
 };

template <template <typename...> class Iterable, typename ... Ts>
A::A (Iterable<int, Ts...> const & it) : l{std::cbegin(it), std::cend(it)}
 { }

int main ()
 {
   std::list<int> l {1, 2, 3, 4};
   A a(l);

   for (auto i : a.l)
      std::cout << i << " ";
   std::cout << std::endl;

   A b {{1, 2, 3, 4}};

   for (auto i : b.l)
      std::cout << i << " ";
   std::cout << std::endl;
 }
Другие вопросы по тегам