提问人:fredoverflow 提问时间:9/13/2013 更新时间:10/11/2013 访问量:786
通过 using 指令调用 begin 和 end?
Invoking begin and end via using-directive?
问:
调用的既定成语是:swap
using std::swap
swap(foo, bar);
这样,对于命名空间之外的用户定义类型,可以重载。swap
std
我们是否应该以同样的方式调用?begin
end
using std::begin;
using std::end;
some_algorithm(begin(some_container), end(some_container));
或者我们应该写:
some_algorithm(std::begin(some_container), std::end(some_container));
答:
的文档指定您引用的习语是 STL 库中的常见做法swap
标准库的许多组件(在 std 中)调用 swap 允许非基本类型自定义重载的不合格方式 被调用而不是这个通用版本:交换的自定义重载 在与提供它们的类型相同的命名空间中声明 通过与参数相关的查找来选择此泛型 版本。
和 的文档中没有这样的东西。begin
end
出于这个原因,您绝对可以使用
using std::begin;
using std::end;
some_algorithm(begin(some_container), end(some_container));
调用约定,但您必须知道,这是一个约定,不适用于例如标准算法,而仅适用于您的代码。
评论
使用这样的声明是 IMO 的正确方式。这也是标准对 for 循环范围所做的:如果没有 or 成员,那么它将调用 and with 作为关联的命名空间(即,它将找到 and 如果 ADL 找不到非成员和 )。using
begin
end
begin(x)
end(x)
std
std::begin
std::end
begin
end
如果你觉得一直写很乏味,那么你可以使用下面的和函数:using std::begin; using std::end;
adl_begin
adl_end
namespace aux {
using std::begin;
using std::end;
template<class T>
auto adl_begin(T&& x) -> decltype(begin(std::forward<T>(x)));
template<class T>
auto adl_end(T&& x) -> decltype(end(std::forward<T>(x)));
template<class T>
constexpr bool is_array()
{
using type = typename std::remove_reference<T>::type;
return std::is_array<type>::value;
}
} // namespace aux
template<class T,
class = typename std::enable_if<!aux::is_array<T>()>::type>
auto adl_begin(T&& x) -> decltype(aux::adl_begin(std::forward<T>(x)))
{
using std::begin;
return begin(std::forward<T>(x));
}
template<class T,
class = typename std::enable_if<!aux::is_array<T>()>::type>
auto adl_end(T&& x) -> decltype(aux::adl_end(std::forward<T>(x)))
{
using std::end;
return end(std::forward<T>(x));
}
template<typename T, std::size_t N>
T* adl_begin(T (&x)[N])
{
return std::begin(x);
}
template<typename T, std::size_t N>
T* adl_end(T (&x)[N])
{
return std::end(x);
}
这段代码非常可怕。希望有了C++14,这可以变得不那么神秘:
template<typename T>
concept bool Not_array()
{
using type = std::remove_reference_t<T>;
return !std::is_array<type>::value;
}
decltype(auto) adl_begin(Not_array&& x)
{
using std::begin;
return begin(std::forward<Not_array>(x));
}
decltype(auto) adl_end(Not_array&& x)
{
using std::end;
return end(std::forward<Not_array>(x));
}
template<typename T, std::size_t N>
T* adl_begin(T (&x)[N])
{
return std::begin(x);
}
template<typename T, std::size_t N>
T* adl_end(T (&x)[N])
{
return std::end(x);
}
评论
begin(T)
std::begin
std::end
begin
end
std::begin
如果您的some_container是标准容器,则 std:: 前缀是不必要的
#include <iostream>
#include <vector>
#include <algorithm>
int main(){
std::vector<int>v { 1, 7, 1, 3, 6, 7 };
std::sort( begin(v), end(v) ); // here ADL search finds std::begin, std::end
}
评论
std::
sort
::std::
::
std::sort
std::vector::iterator
std::sort
免責聲明:对于迂腐的类型(或迂腐的类型,如果你想变得迂腐......),我通常在这里将“重载”一词称为“使用 std::begin 创建名称为开头
和结尾
的函数,并执行;使用 std::end;
.“,相信我,这对我来说一点也不乏味,但很难阅读,阅读起来是多余的。:p.
我基本上会给你这种技术的可能用例,以及我的结论。
案例 1 - 您的 和 方法的行为与标准容器的行为不同begin
end
可能需要重载 and 函数的一种情况是,当您以不同的方式使用类型的 and 方法时,而不是提供对对象元素的类似迭代器的访问,并且希望重载 和 调用用于迭代的 begin 和 end 方法。std::begin
std::end
begin
end
std::begin
std::end
struct weird_container {
void begin() { std::cout << "Start annoying user." }
void end() { std::cout << "Stop annoying user." }
iterator iter_begin() { /* return begin iterator */ }
iterator iter_end() { /* return end iterator */ }
};
auto begin(weird_container& c) {
return c.iter_begin();
}
auto end(weird_container& c) {
return c.iter_end();
}
但是,您不会也不应该做这样疯狂的事情,因为如果与对象一起使用,则 range-for 会中断,根据 range-for 的规则,and 方法将在独立函数变体之前找到。weird_container
weird_container::begin()
weird_container::end()
因此,这种情况提出了一个论点,即不要使用您提出的内容,因为它会破坏该语言的一个非常有用的功能。
案例 2 - 根本没有定义方法begin
end
另一种情况是未定义 and 方法。这是一种更常见和适用的情况,当您希望在不修改类接口的情况下扩展类型以使其可迭代时。begin
end
struct good_ol_type {
...
some_container& get_data();
...
};
auto begin(good_ol_type& x) {
return x.get_data().begin();
}
auto end(good_ol_type& x) {
return x.get_data().end();
}
这将使您能够在(算法、范围等)上使用一些漂亮的功能,而无需实际修改其界面!这与 Herb Sutter 的建议一致,即通过非成员非友元函数扩展类型的功能。good_ol_type
这是一个很好的情况,你实际上想要重载和 .std:;begin
std::end
结论
由于我从未见过有人做过类似第一种情况的事情(除了我的例子),那么你真的想使用你建议的东西并重载并在适用的情况下。std::begin
std::end
我在这里没有包括您同时定义 and 方法和 和 函数的情况,这些函数的作用与方法不同。我相信这种情况是人为的、格式错误的和/或由没有太多经验深入研究调试器或阅读新模板错误的程序员完成的。begin
end
begin
end
评论
std
评论