使用模板模板参数扣除参数

argument deduction with template template parameters

本文关键字:参数      更新时间:2023-10-16

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

由于集合可以是任何类型,因此我使用template-template参数来指定集合类型。但是,它始终是string的集合。我希望模板参数扣除可以工作,以便在调用成员函数时不必指定集合类型。

SSCCE随后的代码类似于我的预期用例。

到目前为止,我有班级定义(实时演示):

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

最终我真正关心的只是能够在string集合中使用始终和结束迭代器调用DoIt。实际类型的集合可以是vectorlist,我不想指定模板参数,也不想基于容器过载。

我该如何工作?

请注意,我的实际用例将在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_convertiblestring的任何一对迭代器(在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对我来说无法尽快到达这里。

问题是,对于非const字符串beginend函数返回string::iterator,而不是string::const_iterator。您应该仅使用string::iterator编写类似的功能。或者,在C 11中,您可以使用CBEGIN和CEND函数。

这是一个未偿还的上下文,您无法处理。

给定一个迭代器,您只是无法分辨它属于哪种容器。原始指针可以简单地用作多种容器的迭代器。

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

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

不,你没有。谁告诉您Foo<string>::iterator对字符串(或者就此存在)的dere键?当然,如果存在,它可能会 becsuse becsuse over现有惯例,但这绝不可以保证。