在C++ 11中经常需要定义一个以容器作为参数的函数.
例如,让我们定义一个函数addup(是的只是一个简单的版本std::accumulate):
template <class I>
int addup (I first, I last)
{
int x = 0;
while ( first != last )
x += *first++;
return x;
}
Run Code Online (Sandbox Code Playgroud)
这需要一个迭代器范围,这是灵活的标准库惯用法.
但是假设我有一个功能:
vector<T> f();
Run Code Online (Sandbox Code Playgroud)
我必须这样做:
auto v = f();
int x = addup(v.begin(), v.end());
Run Code Online (Sandbox Code Playgroud)
我宁愿这样做:
int x = addup(f());
Run Code Online (Sandbox Code Playgroud)
就像我可以这样做:
for (auto t : f())
...
Run Code Online (Sandbox Code Playgroud)
本着基于范围的精神,我想要这样的事情:
template<class C>
int addup(C&& container)
{
addup(beginexpr(container), endexpr(container)); // ???
}
Run Code Online (Sandbox Code Playgroud)
在标准中它在6.5.4(释义)中说:
(A)if
container是数组类型,beginexpr并且endexpr分别是container和container+bound,bound数组绑定的位置.(B)如果
container是一个类型时,不合格的IDSbegin和end被查找类的范围container由类成员访问查找(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);
Run Code Online (Sandbox Code Playgroud)
那我们可以静态断言(使用"SFINAE"或类似的)模板参数必须是迭代器,数组,容器类等 - 并且这些信息用于过载消歧吗?
Pub*_*bby 11
这就是我要做的:
template<class Range>
int addup(Range&& range)
{
using std::begin;
using std::end;
addup(begin(range), end(range)); // begin(), NOT std::begin() (ADL)
}
Run Code Online (Sandbox Code Playgroud)
它将处理所有重要的情况并正确地进行ADL.我不确定它是否等同于基于范围的for,但在我看来它是最好的解决方案.
以下两个调用含糊不清:
我没有编译,但除非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));
}
Run Code Online (Sandbox Code Playgroud)