一般开始,加上考虑本地使用声明的 decltype
C++0x 的 ranged-for 循环有一个处理数组的特殊例外(FDIS §6.5.4),并且有两个函数 std::begin 和 end,它们被重载以处理数组或选择 begin/end 方法。这让我相信可以编写接受通用序列的函数来匹配 ranged-for 循环的行为:
template<class C>
void f(C &c) {
using std::begin;
using std::end;
do_something_with(begin(c), end(c));
}
如果 C 的命名空间中有“更具体”的开始/结束,则将通过 ADL 选择它,否则代码“默认”为 std::begin/end。
然而,有一个特殊的例外。如果在命名空间中传递具有语义不同的开始/结束(采用指针)的类型数组,则不会选择 std::begin/end 的数组形式:
namespace ns {
struct A {};
void begin(A*); // Does something completely different from std::begin.
}
void f_A() { // Imagine above f() called with an array of ns::A objects.
ns::A c[42];
using std::begin;
begin(c); // Selects ns::begin, not array form of std::begin!
}
为了避免这种情况,是否有比编写更好的解决方案我自己的开始/结束包装器(内部使用 ADL)并显式调用它们而不是 std::begin 或 ADLized 开始?
namespace my {
template<class T>
auto begin(T &c) // Also overload on T const &c, as std::begin does.
-> decltype(...) // See below.
{
using std::begin;
return begin(c);
}
template<class T, int N>
T* begin(T (&c)[N]) {
return c;
}
}
// my::end omitted, but it is analogous to my::begin.
template<class C>
void f(C &c) {
do_something_with(my::begin(c), my::end(c));
}
然而,如上面的省略号所示,我什至不知道如何编写 my::begin!对于该 decltype,我如何选择将通过本地 using 声明和 ADL 选择的类型?
C++0x's ranged-for loop has a special exception to handle arrays (FDIS §6.5.4), and there are two functions, std::begin and end, which are overloaded to handle arrays or to select begin/end methods. This leads me to believe a function accepting a generic sequence could be written to match a ranged-for loop's behavior:
template<class C>
void f(C &c) {
using std::begin;
using std::end;
do_something_with(begin(c), end(c));
}
If there's a "more specific" begin/end in the namespace of C, it will be selected through ADL, otherwise the code "defaults" to std::begin/end.
However, there is a reason ranged-for has that special exception. If passing an array of a type in a namespace with a semantically-different begin/end which takes a pointer, the array forms of std::begin/end aren't selected:
namespace ns {
struct A {};
void begin(A*); // Does something completely different from std::begin.
}
void f_A() { // Imagine above f() called with an array of ns::A objects.
ns::A c[42];
using std::begin;
begin(c); // Selects ns::begin, not array form of std::begin!
}
To to avoid this, is there a better solution than writing my own begin/end wrappers (which use ADL internally) and calling them explicitly instead of either std::begin or an ADLized begin?
namespace my {
template<class T>
auto begin(T &c) // Also overload on T const &c, as std::begin does.
-> decltype(...) // See below.
{
using std::begin;
return begin(c);
}
template<class T, int N>
T* begin(T (&c)[N]) {
return c;
}
}
// my::end omitted, but it is analogous to my::begin.
template<class C>
void f(C &c) {
do_something_with(my::begin(c), my::end(c));
}
However, as shown by the ellipsis above, I don't even know how to write my::begin! How can I, for that decltype, select the type that will be selected through a local using-declaration and ADL?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我在使用元组时遇到了同样的情况:
它接受
std::tuple
和boost::tuple
,并且接受左值和右值,而不是模板<类型名称...类型> auto f(std::tuple<类型...>&元组) -> /* ??? */。
这种特殊情况是通过特征类解决的,该特征类实际上是由标准提供的:
std::tuple_element
。与特征类一样,我们的想法是元组是一个协议,任何想要遵守它的东西都会为例如元组元素提供专门化。所以就我而言,解决方案已经存在。就您而言,如果您正在编写一个库,我建议您编写(并记录)这样一个特征类。在应用程序代码或其他情况下,我不太确定。
I've encountered the same situation while using tuples:
which accepts both
std::tuple
andboost::tuple
, and accepts both lvalues and rvalues as opposed totemplate<typename... Types> auto f(std::tuple<Types...>& tuple) -> /* ??? */
.This particular case was solved with a traits class, which is in fact provided by the Standard:
std::tuple_element
. As usual with traits classes, the idea is thattuple
is a protocol and anything that want to conform to it will provide a specialization for e.g.tuple_element
. So in my case the solution already existed.In your case, if you were writing a library, I'd recommend writing (and documenting) such a traits class. In application code or other situations, I'm not so sure.
您可以自己对数组进行特殊处理。数组的类型是(并且必须是begin/end才能工作)
ElementType (&)[Size]
,因此如果您重载该函数,例如:它的行为应该特别类似于 for-环形。
顺便说一句,您不需要
std::begin
和std::end
那么,它们是微不足道的:(可能需要强制转换;我实际上只使用了它与需要指针的东西一起使用,而不是任何迭代器)。
另一方面,使用指针的
begin
和end
函数是相当愚蠢的事情。如果指向的对象是一个集合,那么它们可能应该采用引用。You can special-case the arrays yourself. The type of an array is (and has to be for begin/end to work)
ElementType (&)[Size]
, so if you overload the function like:it should behave specially like the for-loop.
On a side-note, you don't need
std::begin
andstd::end
then, they are trivial:(may need a cast; I actually only used it with things that demanded pointers, not any iterators).
On another side-note,
begin
andend
functions taking pointers are rather silly thing to do. If the pointed object is a collection, they should probably be taking reference instead.