fre*_*low 16 c++ stl using-directives argument-dependent-lookup c++11
已经确定的调用习惯swap是:
using std::swap
swap(foo, bar);
Run Code Online (Sandbox Code Playgroud)
这样,swap可以为std命名空间之外的用户定义类型重载.
我们应该调用begin,并end以同样的方式?
using std::begin;
using std::end;
some_algorithm(begin(some_container), end(some_container));
Run Code Online (Sandbox Code Playgroud)
或者我们应该写:
some_algorithm(std::begin(some_container), std::end(some_container));
Run Code Online (Sandbox Code Playgroud)
使用using- 这样的声明是IMO的正确方法.这也是什么标准并与循环范围:如果没有begin或end存在,那么它会调用成员begin(x),并end(x)与std作为关联的命名空间(即它会找到std::begin并且std::end如果ADL没有找到非会员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);
}
Run Code Online (Sandbox Code Playgroud)
这段代码非常可怕.希望通过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);
}
Run Code Online (Sandbox Code Playgroud)
免责声明:对于迂腐类型(或悬挂物,如果你想成为迂腐......),我通常是指词"过载"这里"创建有名称的功能begin和end做using std::begin; using std::end;." 相信我,对我来说,写作并不乏味,但阅读起来很难阅读.:p.
我基本上会给你这种技术的可能用例,后来我的结论.
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();
}
Run Code Online (Sandbox Code Playgroud)
但是,你不会也不应该做如此疯狂的事情,因为如果与一个对象一起使用,它将会破坏weird_container,根据范围规则,for weird_container::begin()和weird_container::end()方法将在独立函数变体之前找到.
因此,这个案例提出了一个论据,即不要使用你提出的建议,因为它会打破一种非常有用的语言特征.
begin和end方法不是在所有定义另一种情况是你没有定义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();
}
Run Code Online (Sandbox Code Playgroud)
这将使您能够使用一些漂亮的功能good_ol_type(算法,范围等),而无需实际修改其界面!这符合Herb Sutter关于通过非成员非朋友功能扩展类型功能的建议.
这是很好的情况下,一个在那里你真的想超载std:;begin和std::end.
由于我没有以往见过有人做这样的事情在第一种情况下(除了我的例子),那么你真的想使用你提出什么,过载std::begin及std::end(如适用).
我没有在这里列出你定义了两者begin和end方法的情况,以及begin与方法end不同的函数.我相信这样的情况是由程序员设计的,形成不良和/或完成的,他们没有多少经验深入研究调试器或阅读新颖的模板错误.