C++11:用容器参数定义函数(类似于基于范围的)
C++11: Defining Function With Container Parameter (like range-based for)?
在C++11中,我经常需要定义一个将容器作为参数的函数。
例如,让我们定义一个函数addup
(是的,只是std::accumulate
的一个简单版本):
template <class I>
int addup (I first, I last)
{
int x = 0;
while ( first != last )
x += *first++;
return x;
}
这需要一个迭代器范围,它是灵活的,也是标准的库习惯用法。
然而,假设我有一个函数:
vector<T> f();
我必须这样做:
auto v = f();
int x = addup(v.begin(), v.end());
我宁愿这样做:
int x = addup(f());
就像我能做到的那样:
for (auto t : f())
...
本着基于范围的精神,我想要这样的东西:
template<class C>
int addup(C&& container)
{
addup(beginexpr(container), endexpr(container)); // ???
}
在标准6.5.4中(转述):
(A)如果
container
是数组类型,则beginexpr
和endexpr
分别是container
和container
+bound
,其中bound
是数组绑定。(B) 如果
container
是类类型,则在类container
的作用域中查找不合格idbegin
和end
,就像通过类成员访问查找一样(3.4.5),并且如果其中一个(或两者)找到至少一个声明,则beginexpr
和endexpr
分别是container.begin()和container.end();(C) 否则,
beginexpr
和endexpr
分别为begin(container)
和end(container)
,其中开始和结束是通过自变量相关查找查找的(3.4.2)
是否可以定义addup
的一组重载或专门化,使其能够处理这四种情况,而不与其他重载冲突?这首先是一个正则迭代器对函数,然后是上面的a、B和C中的每一个。怎样
(如果这是可能的,为什么标准库不提供这样的过载?)
此外,如果函数在容器之外获取额外的参数,该怎么办?我们是否可以修改重载,使添加到所有重载中的可选额外参数x
(一个具有默认值的参数)不会使以下两个调用不明确:
addup(v.begin(), v.end());
addup(v, x);
也就是说,我们可以静态地断言(使用"SFINAE"或类似的方法)模板参数必须是迭代器、数组、容器类等,并将这些信息用于过载消除歧义吗?
这就是我要做的:
template<class Range>
int addup(Range&& range)
{
using std::begin;
using std::end;
addup(begin(range), end(range)); // begin(), NOT std::begin() (ADL)
}
它将处理所有重要的病例,并正确地进行ADL。我不确定它是否等同于基于range-based的功能,但在我看来,这是最好的解决方案。
以下两个调用不明确:
我还没有编译,但除非x
需要隐式转换,否则我看不到任何歧义。您还可以使用boost::make_iterator_range
,避免迭代器参数过载。
我认为这也会起作用:
template<class Range>
int addup(Range&& range)
{
int x = 0;
for(auto&& v : range)
x += v;
return x;
}
template <class I>
int addup (I first, I last)
{
return addup(boost::make_iterator_range(first, last));
}
几种情况:
- 如果没有任何其他参数,则可以使用
std::begin
和std::end
处理所有情况 - 还有一个额外的参数,其类型不是模板,或者依赖于您的范围/迭代器(例如
T::value_type
将同时在范围和迭代器上工作),或者具有默认值。那就没有问题了 - 还有一个附加参数,它与范围无关,其类型是模板,没有默认值。然后,在调用函数时,如果不手动指定该类型,就无法执行此操作
下面是一个例子:
template <class Thing, class Iterator>
void DoStuff(Iterator first, Iterator last, Thing thing = Thing())
{ ... }
template <class Thing, class Range>
void DoStuff(Range& range, Thing thing = Thing())
{ ... }
vector<int> coin = {1,2,3,4};
DoStuff(coin, 123); // OK
DoStuff(begin(coin), end(coin), 123); // OK
DoStuff<int>(coin); // OK
DoStuff<int>(begin(coin), end(coin)); // OK
DoStuff(coin); // !! KO
DoStuff(begin(coin), end(coin)); // !! KO
如果用typename Range::value_type
或int
替换Thing
(并将其移动到模板参数列表中的第二个位置),则所有重载都将起作用。您也可以为Thing
指定一个默认值。
- 类似于strcat()的函数出现问题
- 如何将C++闭包与变量参数同时重用——类似于JavaScript
- 算术运算的结果类似于:C浮点变量中的1/3
- 为什么我的变量存在于其范围之外
- 在数组中输入 n 个整数的列表,并以类似于钟摆来回移动的方式排列它们. 输入-1 3 2 5 4,输出5 3 1 2 4
- 创建类似于布尔值的变量类型
- 如何在 Arduino 字符串的开头添加元素.类似于 JS unshift();
- Java 中是否有类似于 C++ 中引用类型"&"的内容?
- C++参数类型以接受适用于基于范围的 for 循环的所有序列
- C++:将参数应用于函数范围
- C++11:用容器参数定义函数(类似于基于范围的)
- 在C /STL中,是否有相当于Python范围()的紧凑型
- std::copy_n 是否适用于重叠范围?
- 在现代C++中,有没有类似于python中基于范围的“enumerate”循环
- 标准算法 any_of()、all_of() 和 none_of() 应用于空范围
- 有没有一个类似/等价于Functional Java的C++库
- 常数,但仅适用于此范围的剩余部分
- 确定谓词是否适用于一个范围的全部、部分或全部元素
- 延长临时的生命周期,适用于块范围的聚合,但不是通过"新";为什么?
- 类似于变换的升压范围适配器,其可以访问相邻元件