【问题标题】:Accept templated parameter of stl_container_type<string>::iterator接受 stl_container_type<string>::iterator 的模板化参数
【发布时间】:2011-02-24 17:24:41
【问题描述】:

我有一个函数,其中我有一个包含字符串的容器(例如vector&lt;string&gt;set&lt;string&gt;list&lt;string&gt;),并且给定一个开始迭代器和一个结束迭代器,遍历处理字符串的迭代器范围。

目前函数是这样声明的:

template< typename ContainerIter>
void ProcessStrings(ContainerIter begin, ContainerIter end);

现在这将接受任何符合实现 operator* 的隐式接口、前缀 operator++ 以及函数体中的任何其他调用的任何类型。

我真正想做的是有一个像下面这样明确限制输入量的定义(伪代码警告):

template< typename Container<string>::iterator>
void ProcessStrings(Container<string>::iterator begin, Container<string>::iterator end);

这样我就可以这样使用它:

vector<string> str_vec;
list<string> str_list;
set<SomeOtherClass> so_set;

ProcessStrings(str_vec.begin(), str_vec.end());  // OK
ProcessStrings(str_list.begin(), str_list.end());  //OK
ProcessStrings(so_set.begin(), so_set.end());  // Error

本质上,我要做的是限制函数规范,使函数的用户清楚它接受的内容,如果代码编译失败,他们会收到一条消息,表明他们使用了错误的参数类型,而不是在函数体中找不到 XXX 类的 XXX 函数。

【问题讨论】:

  • 您希望将其限制为std::string 或任何std::basic_string 类型吗?
  • 啊,确实有人需要 概念!不幸的是,去年夏天,在 C++ 标准化委员会一位相当重要的成员的推荐下,他们被抛弃了,因为他们推迟了 C++0x 的到来,以至于“x”变成了十六进制数字。
  • 为什么需要限制可以调用模板的内容?模板的重点是能够重用构造良好的算法。
  • @jmucchiello:通常的动机是更明确地说明您的限制,并为不符合它们的类型获得更好的错误消息。
  • 是的,我乍一看并没有理解,但问题的目标并不是真正的限制,而是一个不跨越 7/8 行并让用户感到困惑的错误消息。概念是解决它的最佳方法......不幸的是,Boost.Concept 文档确实缺乏:/ 使用 BOOST_MPL_ASSERT_MSG 应该提供足够好的错误消息(至少比正常错误更好),但它比必要的限制更多:)

标签: c++ templates iterator


【解决方案1】:

您可以使用boost::enable_if 模板魔法来实现此类检查。除非迭代器的值类型是字符串类型,否则下面的方法不会被编译。

template<class It>
boost::enable_if_c< 
        boost::is_same< typename boost::iterator_value<It>::type, string >::value
>::type
ProcessStrings(It itBegin, It itEnd) 
{ }

如果boost::iterator_value&lt;It&gt;::type 是字符串类型,boost::enable_if&lt;...&gt;::type 将评估为 void,即您的返回参数。否则,由于 SFINAE(substitution-failure is not an error)原则,该方法将不会在没有错误的情况下编译。

【讨论】:

  • 函数体内的enable_ifstatic_assert 应该可以工作。删除void 返回类型(在此处指定它是enable_if 的工作)。
  • @Matthieu 谢谢,你是对的。 void 返回值是一个错字。
【解决方案2】:

一个简单但有效的方法。

template <class T>
struct is_basic_string: boost::mpl::false_ {};

template <class CharT, class Traits, class Alloc>
struct is_basic_string< std::basic_string<CharT, Traits, Alloc> >:
   boost::mpl::true_ {};

然后用它来检查值类型

void ProcessStrings(Iterator begin, Iterator end)
{
  BOOST_MPL_ASSERT_MSG(
    is_basic_string< typename boost::value_type<Iterator>::type >,
    ONLY_ACCEPT_ITERATOR_TO_BASIC_STRING,
    (Iterator)
  );

  // do your work
}

查看参考here,此宏旨在在编译时提供尽可能有意义的消息。

这也比Sebastian 解决方案更通用...但std::wstring 对国际化非常方便,你不会想窒息的。

现在很好的问题是……你为什么要这样做!

泛型编程的真正目标是生成可以与任何符合它们内部使用的操作的类型一起工作的函数。为什么要故意限制这一点?

【讨论】:

  • 感谢您提供有关使用 basic_string 的更多详细信息,这是对该问题的一个有趣的扩展。我已经回复了@jmucchiello 对原始问题的评论,我希望在最后一段中为您的问题提供某种解释。
【解决方案3】:

试试

#include <string>
#include <vector>
#include <list>

template<template<typename T,typename A> class C>
void ProcessStrings(typename C<std::string, std::allocator<std::string> >::iterator begin,
                    typename C<std::string, std::allocator<std::string> >::iterator end)
{
}


int main()
{
    std::vector<std::string>    strVec;
    std::list<std::string>      strList;
    std::list<int>              intList;

    ProcessStrings<std::vector>(strVec.begin(), strVec.end());
    ProcessStrings<std::list>(strList.begin(), strList.end());
    ProcessStrings<std::list>(intList.begin(), intList.end());  // This will fail
}

【讨论】:

  • 您能否详细说明此答案与迈克的不同之处?我看到它指定了分配器参数,但这真的是一个关键的区别吗?
  • 我发帖时没有看到 Mikes。但是我只是尝试过,如果不明确指定容器模板中的所有类型参数,我似乎无法让它工作。我不知道这是我的编译器还是其他我没有尝试过解决问题的东西。
【解决方案4】:

您可以使用模板模板参数来接近这一点:

template<template<class> class CONTAINER>
void ProcessStrings(CONTAINER<string>&);

这将处理整个容器,如果它不包含字符串,则会出现编译错误。

ProcessStrings(str_vec); // OK
ProcessStrings(so_set); // Error

如果你想使用迭代器范围,那么我能管理的最好的就是

template<template<class> class CONTAINER>
void ProcessStrings(typename CONTAINER<string>::iterator, 
                    typename CONTAINER<string>::iterator);

不幸的是,类型推断不适用于函数参数,因此您必须显式提供模板参数:

ProcessStrings<vector>(str_vec.begin(), str_vec.end()); // OK
ProcessStrings<set>(so_set.begin(), so_set.end()); // Error

有人可以改进吗?

【讨论】:

  • 我会很惊讶它起作用了,使用 template &lt;class&gt; CONTAINER 很麻烦,因为您需要指定模板参数的确切数量,而且不仅有几个取决于我们正在谈论的容器的参数,而且还有还有一个问题是 STL 实现可能会添加一些补充参数,只要它们具有适当的默认值....
  • @Matthieu:我很确定模板参数推导可以填充默认参数以找到匹配项。当然,当我用 gcc 测试这段代码时,它是有效的。
猜你喜欢
  • 1970-01-01
  • 2021-10-18
  • 2019-11-22
  • 2018-05-12
  • 1970-01-01
  • 1970-01-01
  • 2014-08-05
  • 2018-01-29
  • 1970-01-01
相关资源
最近更新 更多