У меня есть шаблон класса. В этом шаблоне класса я пытаюсь определить шаблон функции-члена, который принимает const_iterator
на коллекции string
s. Сама коллекция может быть любой коллекцией StdLib, но реально это будет либо vector
или list
,
Поскольку коллекция может быть любого типа, я использую template-template
Параметр для указания типа коллекции. Однако это всегда будет коллекция string
, Я хочу, чтобы вывод аргумента шаблона работал так, чтобы мне не приходилось указывать тип коллекции при вызове функции-члена.
Код, который следует в SSCCE, напоминает мой предполагаемый вариант использования.
Пока что я за определение класса (Live Demo):
template <typename Foo>
struct Gizmo
{
Foo mF;
Gizmo (Foo f) : mF (f) {};
template <template <typename> class Cont> void DoIt(
typename Cont <string>::const_iterator begin,
typename Cont <string>::const_iterator end)
{
stringstream ss;
ss << "(" << this->mF << ")\n";
const std::string s = ss.str();
copy (begin, end, ostream_iterator <std::string> (cout, s.c_str()));
}
};
Компиляция экземпляров шаблона класса завершается успешно:
int main()
{
list <string> l;
l.push_back ("Hello");
l.push_back ("world");
Gizmo <unsigned> g (42);
}
Однако, когда я пытаюсь использовать вывод аргумента (без которого, все это упражнение почти бессмысленно):
g.DoIt (l.begin(), l.end());
GCC жалуется, что не может вывести аргумент шаблона:
prog.cpp: In function ‘int main()’:
prog.cpp:34:28: error: no matching function for call to ‘Gizmo<unsigned int>::DoIt(std::list<std::basic_string<char> >::iterator, std::list<std::basic_string<char> >::iterator)’
g.DoIt (l.begin(), l.end());
^
prog.cpp:34:28: note: candidate is:
prog.cpp:16:49: note: template<template<class> class typedef Cont Cont> void Gizmo<Foo>::DoIt(typename Cont<std::basic_string<char> >::const_iterator, typename Cont<std::basic_string<char> >::const_iterator) [with Cont = Cont; Foo = unsigned int]
template <template <typename> class Cont> void DoIt(
^
prog.cpp:16:49: note: template argument deduction/substitution failed:
prog.cpp:34:28: note: couldn't deduce template parameter ‘template<class> class typedef Cont Cont’
g.DoIt (l.begin(), l.end());
в конечном счете все, что меня действительно волнует, это возможность звонить DoIt
с начала и конца итераторов на коллекции string
, Фактический тип коллекции может быть vector
или же list
и я не хочу ни указывать аргументы шаблона, ни перегрузку на основе контейнера.
Как я могу заставить это работать?
Обратите внимание, что мой фактический вариант использования будет в C ++ 03. Приветствуются решения C ++ 11, но я смогу принять только решение C ++ 03.
Было несколько проблем. Я исправил параметр шаблона шаблона для вас. Я также изменил сигнатуру метода, чтобы вы могли автоматически выводить типы, но это требует передачи исходной коллекции:
#include <iostream>
#include <vector>
#include <list>
#include <string>
#include <sstream>
#include <iterator>
using namespace std;template <typename Foo>
struct Gizmo
{
Foo mF;
Gizmo (Foo f) : mF (f) {};
template <template <typename T, typename A = allocator<T> > class Cont> void DoIt(
const Cont <string> &, // deduction
const typename Cont <string>::iterator &begin,
const typename Cont <string>::iterator &end)
{
stringstream ss;
ss << "(" << this->mF << ")\n";
const std::string s = ss.str();
copy (begin, end, ostream_iterator <std::string> (cout, s.c_str()));
}
};
int main()
{
list <string> l;
l.push_back ("Hello");
l.push_back ("world");
Gizmo <unsigned> g (42);
g.DoIt (l, l.begin(), l.end());
}
Кажется, я упускаю суть, но почему ты не можешь сделать это?
template <typename Iterator> void DoIt(
Iterator begin,
Iterator end)
{
// snip
}
// [...]
list <string> l;
l.push_back ("Hello");
l.push_back ("world");
vector <string> v;
v.push_back ("Hello");
v.push_back ("world");
Gizmo <unsigned> g (42);
g.DoIt (l.begin(), l.end());
g.DoIt (v.begin(), v.end());
Я утверждаю, что вам на самом деле все равно, если ваш вклад vector
или list
или даже контейнер. Я думаю, что все, что вас на самом деле беспокоит, это то, что у вас есть последовательность вещей, которую вы можете перебрать, которые можно преобразовать string
, Таким образом, вы должны принять любую пару итераторов, чьи value_type
is_convertible
в string
(Живая демоверсия в Coliru):
template <typename Iter>
typename enable_if<
is_convertible<
typename iterator_traits<Iter>::value_type,string
>::value
>::type DoIt(Iter begin, Iter end) const
{
stringstream ss;
ss << "(" << this->mF << ")\n";
const std::string s = ss.str();
copy (begin, end, ostream_iterator <std::string> (cout, s.c_str()));
}
Я прошу прощения за уродство ограничения, Понятия Lite не могу добраться сюда достаточно скоро для меня.
Проблема в том, что для неконстантных строк begin
а также end
возврат функций string::iterator
, но нет string::const_iterator
, Вы должны написать аналогичную функцию, только с string::iterator
, Или в c ++ 11 вы можете использовать функции cbegin и cend.
Это не выводимый контекст, и вы не можете обойти это.
Учитывая итератор, вы просто не можете сказать, к какому контейнеру он принадлежит. Например, необработанный указатель может служить итератором для нескольких типов контейнеров.
К счастью, вы никогда не используете тип контейнера для чего-либо, поэтому вы можете просто выбросить его и параметризировать на итераторе.
Но я использую его, чтобы убедиться, что мой контейнер содержит строки!
Нет, ты не. Кто сказал тебе Foo<string>::iterator
разыменование строки (или что она существует, в этом отношении)? Конечно, если это существует, это наверное разыменовывается в строку из-за существующих соглашений, но это ни в коем случае не гарантируется.