参数推导与模板模板参数

时间:2014-02-03 17:55:14

标签: c++ templates template-templates argument-deduction

我有一个班级模板。在这个类模板中,我试图定义一个成员函数模板,该模板在const_iterator的集合上接受string个。集合本身可以是任何类型的StdLib集合,但实际上它可以是vectorlist

由于集合可以是任何类型,我使用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。实际的集合类型可以是vectorlist,我不想指定模板参数,也不想基于容器重载。

我怎样才能让它发挥作用?

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

5 个答案:

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

See it run here.

答案 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)

我提交您实际上并不关心您的输入是vectorlist,还是容器。我认为你真正关心的是你有一系列可以迭代的东西,可以转换为string。所以你应该接受value_type is_convertiblestringLive 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字符串beginend函数返回string::iterator,而不是string::const_iterator。您应该只使用string::iterator编写类似的函数。或者,在c ++ 11中,您可以使用cbegin和cend函数。

答案 4 :(得分:0)

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

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

幸运的是,你永远不会将容器类型用于任何东西,所以你可以把它扔掉并在迭代器上进行参数化。

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

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