每个范围类型的模板专业化 [英] Template Specialization for each Range Type

查看:144
本文介绍了每个范围类型的模板专业化的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

背景



在C ++ 11中,基于范围的for循环处理三种范围,概述了 here(link)





$ b for(range_declaration:range_expression)loop_statement



说明



上述语法生成类似于以下代码的代码( __ range __ begin __ end 仅适用于exposition):

  {
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_expr end_expr 定义为:




  • 如果(__ range)是数组,则(__ range)(__ range + __bound),其中 __ bound 是数组绑定;

  • code>(__ range)是一个类,并且具有begin或end成员(或两者),则 begin_expr __ range.begin() end_expr __ range.end()

  • 否则, begin(__ range) end(__ range)是根据与 std 作为关联命名空间的参数相关查找规则找到的。




问题



如何使用三个特殊化来处理完全相同的三种情况在引用的项目符号列表中?



我在想类似以下内容,但我不知道如何正确地这样做。

  //处理第三个项目符号 - 默认情况
模板< typename Range>
void f(Range& range)
{
for(auto it = begin(range); it!= end(range); ++ it)
{
T& item = * it;
/ *做一些自定义的项目* /
}
}

//处理第一个项目符号 - range是一个数组
template< >
void f< T []>(T [] range)
{
auto end = range + sizeof(range)/ sizeof
for(auto it = range; it!= end; ++ it)
{
T& item = * it;
/ *做一些自定义的项目* /
}
}

//使用begin和/或end处理第二个项目符号 - function
template<>
void f< RangeBE>(RangeBE& range)
{
//不知何故限制类型RangeBE到有
的类//开始或结束成员函数...
//可以使用enable_if吗?

for(auto it = range.begin(); it!= range.end(); ++ it)
{
T& item = * it;
/ *做一些自定义的项目* /
}
}

我完全不正确地接近这个?这是否可能与C ++ 11(即,编译器是否做了一些特殊的事情来完成这种特殊化)?请指教我。 :)



一些澄清...



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



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



我的问题是 不是



f 中使用基于范围的循环 > 选项或答案。我实际上想做的是模仿基于范围的for循环的语义,但是由于这个问题的范围之外的并发症,我不能简单地使用基于范围的for循环在示例函数 f 请接受我有充分理由不要只使用基于范围的for循环的事实。

解决方案

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



使用 std :: begin std :: end ,它们已经展示了你期望的语义, em> C ++ 11 。要记住要启用 ADL ,以模仿基于范围的for循环的语义中的第三个项目符号,它最终会显示如下:

 模板< 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;
/ *做一些自定义项目* /
}
}

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

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

这个重载是一个函数模板,它有一个表达式来推导它的返回类型。由于 SFINAE ,当所述表达式在用实际类型替换 C 时导致立即上下文中的错误,所以它被简单地从过载(如果它甚至不存在)

 模板<类T,size_t N> 
T * begin(T(& array)[N]);

此重载处理数组大小写。注意,你的方法是不正确的,因为你正在处理一个不完整大小的数组,并且没有办法你可以得到其中一个元素的数量。


Background

In C++11 the range-based for loop handles three kinds of "ranges," outlined here (link). I've quoted the relevant part below.

Syntax

for (range_declaration : range_expression) loop_statement

Explanation

The above syntax produces code similar to the following (__range, __begin and __end are for exposition only):

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

The range_expression is evaluated to determine the sequence or range will be iterated over. Each element of the sequence is dereferenced, and assigned to the variable using the type and name given in the range_declaration.

The begin_expr and end_expr are defined to be either:

  • If (__range) is an array, then (__range) and (__range + __bound), where __bound is the array bound;
  • If (__range) is a class and has either a begin or end member (or both), then begin_expr is __range.begin() and end_expr is __range.end();
  • Otherwise, begin(__range) and end(__range), which are found based on argument-dependent lookup rules with std as an associated namespace.

Question

How should I write a template function with three specializations that handle the exact same three cases in the quoted bullet list?

I'm thinking something like the following, but I'm not sure how to do this correctly.

//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 */
    }
}

Am I approaching this completely incorrectly? Is this even possible to do with C++11 (i.e., does the compiler get to do something special to accomplish this kind of specialization)? Please enlighten me. :)

Some clarification...

My question is how to handle all three possible types of "range" (from the quoted bullet list) as inputs to a function I write. (I will be using the knowledge gained here to actually write a class qualified with template <typename Range> in the same way, where the class has specializations that handle all three types of ranges.)

My question is not how to write a class that satisfies one of the three possible types of "range" (from the quoted bullet list). There are multiple SO questions about that - this is not a duplicate of those.

My question is not how to use range-based for loops.

"Just use range-based for loops in f" is not an option or an answer. What I'm actually trying to do is mimic the semantics of range-based for loops, but because of complications outside the scope of this question, I can't simply use a range-based for loop inside of the example function f. Please accept the fact that I have good reason to not "Just use a range-based for loop." It would be too complicated to explain why.

解决方案

The easiest way to mimic the semantics of a range-based for loop is to use a range-based for loop. But since your clarification makes that not an option, and we are left wondering what can you possibly intend to do, then there is another approach:

Use std::begin and std::end which already exhibit the semantics you expect, and are written in plain C++11. Remembering to enable ADL to kick in, to mimic the third bullet in the semantics on a range-based for loop, it ends up looking something like this:

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 */
    }
}

If you wonder how std::begin/end work, then just look at their declarations:

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

This overload is a function template that has an expression in it to deduce its return type. Due to SFINAE, when said expression results in an error in the immediate context once replaced C with the actual type, it is simply discarded from the overload set (as if it didn't even existed)

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

This overload handles the array case. Note that your approach is incorrect, as you are dealing with an array of incomplete size, and there is no way you can get the number of elements in one of those.

这篇关于每个范围类型的模板专业化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆