我有一个班级模板。在这个类模板中,我试图定义一个成员函数模板,该模板在const_iterator
的集合上接受string
个。集合本身可以是任何类型的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解决方案。
答案 0 :(得分:2)
有几个问题。我为你修复了模板模板参数。我还修改了方法签名,因此您可以自动推断类型,但它需要传入原始集合:
#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());
}
答案 1 :(得分:1)
好像我错过了这一点,但为什么你不能这样做呢?
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());
答案 2 :(得分:1)
我提交您实际上并不关心您的输入是vector
,list
,还是容器。我认为你真正关心的是你有一系列可以迭代的东西,可以转换为string
。所以你应该接受value_type
is_convertible
到string
(Live demo at 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()));
}
我为约束的丑陋道歉,Concepts Lite不能很快到达我这里。
答案 3 :(得分:0)
问题是非const字符串begin
和end
函数返回string::iterator
,而不是string::const_iterator
。您应该只使用string::iterator
编写类似的函数。或者,在c ++ 11中,您可以使用cbegin和cend函数。
答案 4 :(得分:0)
这是一个非推断的上下文,你无法解决这个问题。
给定一个迭代器,你就不可能知道它属于哪种容器。例如,原始指针可以简单地用作多种类型容器的迭代器。
幸运的是,你永远不会将容器类型用于任何东西,所以你可以把它扔掉并在迭代器上进行参数化。
但我确实使用它来确保我的容器包含字符串!
不,不。谁告诉你Foo<string>::iterator
对字符串的引用(或者它存在,就此而言)?当然,如果它存在,它可能取消引用一个字符串,因为现有的约定,但这绝不是保证。