Ошибка удержания параметров шаблона аргумента шаблона шаблона
У меня возникают проблемы при написании элементов шаблона не шаблонного класса, особенно при выводе параметров шаблона аргумента шаблона шаблона.
Следующий код, минимальный, иллюстрирует мою проблему. Я подозреваю, что там как минимум две разные проблемы.
#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;
}