2

我有一个类模板。在这个类模板中,我试图定义一个成员函数模板,它接受const_iterators 集合上的strings。集合本身可以是任何类型的 StdLib 集合,但实际上它将是 avector或 a 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());

最终,我真正关心的是能够DoItstring. 集合的实际类型可以是vectorlist,我不想指定模板参数,也不想基于容器重载。

我怎样才能让它工作?

请注意,我的实际用例将使用 C++03。欢迎使用 C++11 解决方案,但我只能接受 C++03 解决方案。

4

5 回答 5

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());
}

看到它在这里运行。

于 2014-02-03T18:37:52.530 回答
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());
于 2014-02-03T19:05:42.197 回答
1

我认为您实际上并不关心您的输入是 a vector,还是 a list,甚至是容器。我认为你真正关心的是你有一系列可以迭代的东西,它们可以转换为string. 所以你应该接受任何一对迭代器value_type is_convertiblestringColiru 的现场演示):

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对我来说不能很快到达这里。

于 2014-02-04T05:42:43.813 回答
0

这是一个非推断的上下文,您无法解决这个问题。

给定一个迭代器,你不可能知道它属于哪种容器。例如,原始指针可以作为一种以上容器类型的迭代器。

幸运的是,您从未将容器类型用于任何事情,因此您可能只是将其丢弃并在迭代器上参数化。

但我确实使用它来确保我的容器包含字符串!

你不可以。谁告诉你Foo<string>::iterator取消对字符串的引用(或者它存在,就此而言)?当然,如果它存在,它可能会取消引用到一个字符串,因为现有的约定,但这绝不是保证。

于 2014-02-03T18:52:19.337 回答
0

问题是对于非常量字符串beginend函数 return string::iterator,但不是string::const_iterator。您应该编写一个类似的函数,仅使用string::iterator. 或者,在 c++11 中,您可以使用 cbegin 和 cend 函数。

于 2014-02-03T18:04:21.353 回答