每种范围类型的模板专精

时间:2013-06-18 04:02:26

标签: c++ templates c++11 template-specialization

背景

在C ++ 11中,基于范围的for循环处理三种“范围”,概述为here (link)。我引用了下面的相关部分。

  

语法

for (range_declaration : range_expression) loop_statement
     

说明

     

以上语法生成类似于以下内容的代码(__range__begin__end仅用于展示:

{
     auto && __range = range_expression;
     for (auto __begin = begin_expr,
         __end = end_expr;
         __begin != __end; ++__begin) {
         range_declaration = *__begin;
         loop_statement
     }
}
     

评估range_expression以确定将迭代的序列或范围。序列的每个元素都被解引用,并使用range_declaration中给出的类型和名称分配给变量。

     

begin_exprend_expr被定义为:

     
      
  • 如果(__range)是一个数组,那么(__range)(__range + __bound),其中__bound是数组绑定的;
  •   
  • 如果(__range)是一个类,并且有一个开始或结束成员(或两者),则begin_expr__range.begin()end_expr__range.end();
  •   
  • 否则,begin(__range)end(__range),它们是基于依赖于参数的查找规则找到的,std作为关联的命名空间。
  •   

问题

如何在引用的项目符号列表中使用三个专业化处理完全相同的三种情况来编写模板函数?

我正在考虑以下内容,但我不确定如何正确地做到这一点。

//Handle third bullet - default case
template <typename Range>
void f(Range& range)
{
    for (auto it = begin(range); it != end(range); ++it)
    {
        T& item = *it;
        /* do something custom with item */
    }
}

//Handle first bullet - "range" is an array
template <>
void f<T[]>(T[] range)
{
    auto end = range + sizeof(range)/sizeof(*range);
    for (auto it = range; it != end; ++it)
    {
        T& item = *it;
        /* do something custom with item */
    }
}

//Handle second bullet - "range" with "begin" and/or "end" function
template <>
void f<RangeBE>(RangeBE& range)
{
    //Somehow restrict type RangeBE to classes that have
    //begin or end member functions...
    //Can enable_if be used for this?

    for (auto it = range.begin(); it != range.end(); ++it)
    {
        T& item = *it;
        /* do something custom with item */
    }
}

我完全错误地接近这个吗?这甚至可能与C ++ 11有关(也就是说,编译器是否可以做一些特殊的事情来实现这种专业化)?请赐教。 :)

一些澄清......

我的问题是如何处理所有三种可能类型的“范围”(来自引用的项目符号列表)作为我编写的函数的输入。 (我将使用这里获得的知识以相同的方式实际编写一个使用template <typename Range>限定的类,其中类具有处理所有三种类型范围的特化。)

我的问题是 如何编写一个满足三种可能类型“范围”之一的类(来自引用的项目符号列表)。关于这一点有多个SO问题 - 这不是那些重复。

我的问题是 如何使用基于范围的for循环。

“只需​​在f中使用基于范围的for循环” 一个选项或答案。我实际上要做的是模仿基于范围的for循环的语义,但由于此问题范围之外的复杂性,我不能简单地在示例函数{{1}中使用基于范围的for循环}}。 请接受这样一个事实:我有充分理由不“只使用基于范围的for循环”。要解释原因太复杂了。

1 个答案:

答案 0 :(得分:5)

模仿基于范围的for循环的语义的最简单方法是使用基于范围的for循环。但由于你的澄清使得这不是一个选择,我们不知道你可能打算做什么,那么还有另一种方法:

使用已经展示了您期望语义的std::beginstd::end,并使用简单的 C ++ 11 编写。记住要启用 ADL 以模仿基于范围的for循环的语义中的第三个项目符号,它最终看起来像这样:

template<typename Range>
void f(Range& range)
{
    using std::begin;
    using std::end;

    for(auto it = begin(range), end_it = end(range); it != end_it; ++it)
    {
        T& item = *it;
        /* do something custom with item */
    }
}

如果您想知道std::begin/end是如何工作的,那么只需看看它们的声明:

template< class C >
auto begin( C& c ) -> decltype(c.begin());

此重载是一个函数模板,其中包含一个表达式以推断其返回类型。由于 SFINAE ,当所述表达式在用实际类型替换C时导致直接上下文中的错误时,它只是从过载集中被丢弃(就好像它没有&#39;甚至存在)

template< class T, size_t N >
T* begin( T (&array)[N] );

此重载处理数组大小写。请注意,您的方法不正确,因为您正在处理一个不完整大小的数组,并且您无法获得其中一个元素的数量。