T.C*_*.C. 14 c++ foreach c++11 c++14
考虑基于范围的for循环的begin-expr和end-expr(N4140 [stmt.ranged]/p1)的规范.鉴于一系列__range类型_RangeT,
begin-expr和end-expr确定如下:
- 如果
_RangeT是一个数组类型,开始-EXPR和最终EXPR是__range和__range + __bound分别,其中__bound在阵列的约束.如果_RangeT是未知大小的数组或不完整类型的数组,则该程序格式不正确;- 如果
_RangeT是一个类型时,不合格-ID小号begin和end被查找类的范围_RangeT由类成员访问查找(3.4.5)那样的话,并且如果任一个(或两者)找到至少一个声明,开始-EXPR和end-expr分别是__range.begin()和__range.end();- 否则,开始-EXPR和最终EXPR是
begin(__range)和end(__range)分别在那里begin和end在相关联的命名空间(3.4.2)的查找.[ 注意:不执行普通的非限定查找(3.4.1).- 结束说明 ]
是否可以在普通的C++代码中模拟这种确切的行为?也就是说,我们可以编写一个magic_begin和一个magic_end功能模板
for(auto&& p : range_init) { /* statements */ }
Run Code Online (Sandbox Code Playgroud)
和
{
auto&& my_range = range_init;
for(auto b = magic_begin(my_range), e = magic_end(my_range); b != e; ++b){
auto&& p = *b;
/* statements */
}
}
Run Code Online (Sandbox Code Playgroud)
总是有完全相同的行为?
非答案包括对std::begin/的合格调用std::end(除了其他事项之外不处理第三个子弹),并且using std::begin; begin(range);因为如果ADL begin找到一个同样好的重载,那么这是不明确的std::begin.
为了说明,给出
namespace foo {
struct A { int begin; };
struct B { using end = int; };
class C { int* begin(); int *end(); }; // inaccessible
struct D { int* begin(int); int* end();};
struct E {};
template<class T> int* begin(T&) { return nullptr; }
template<class T> int* end(T&) { return nullptr; }
}
foo::A a; foo::B b; foo::C c; foo::D d; foo::E e;
Run Code Online (Sandbox Code Playgroud)
我希望magic_begin(a)/ magic_begin(b)/ magic_begin(c)/ magic_begin(d)是一个编译错误,并magic_begin(e)返回(int*)nullptr.
Col*_*mbo 12
以下SFINAE友好方法似乎可以按预期工作(参见下面的例外情况):
#include <type_traits>
namespace detail {
struct empty {};
template <typename T>
using base = std::conditional_t<std::is_class<T>{} && not std::is_final<T>{},
T, empty>;
struct P1 {typedef int begin, end;};
template <typename U>
struct TestMemType : base<U>, P1 {
template <typename T=TestMemType, typename=typename T::begin>
static std::true_type test_begin(int);
template <typename T=TestMemType, typename=typename T::end>
static std::true_type test_end(int);
static std::false_type test_begin(float), test_end(float);
};
template <typename T>
constexpr bool hasMember = !decltype(TestMemType<T>::test_begin(0)){}
|| !decltype(TestMemType<T>::test_end(0)){};
//! Step 1
template <typename T, std::size_t N>
constexpr auto begin(int, T(&a)[N]) {return a;}
template <typename T, std::size_t N>
constexpr auto end(int, T(&a)[N]) {return a+N;}
//! Step 2 - this overload is less specialized than the above.
template <typename T>
constexpr auto begin(int, T& a) -> decltype(a.begin()) {return a.begin();}
template <typename T>
constexpr auto end(int, T& a) -> decltype(a.end()) {return a.end();}
//! Step 3
namespace nested_detail {
void begin(), end();
template <typename T>
constexpr auto begin_(T& a) -> decltype(begin(a)) {return begin(a);}
template <typename T>
constexpr auto end_(T& a) -> decltype(end(a)) {return end(a);}
}
template <typename T, typename=std::enable_if_t<not hasMember<std::decay_t<T>>>>
constexpr auto begin(float, T& a) -> decltype(nested_detail::begin_(a))
{return nested_detail::begin_(a);}
template <typename T, typename=std::enable_if_t<not hasMember<std::decay_t<T>>>>
constexpr auto end(float, T& a) -> decltype(nested_detail::end_(a))
{return nested_detail::end_(a);}
}
template <typename T>
constexpr auto magic_begin(T& a) -> decltype(detail::begin(0, a))
{return detail::begin(0, a);}
template <typename T>
constexpr auto magic_end (T& a) -> decltype(detail::end (0, a))
{return detail:: end(0, a);}
Run Code Online (Sandbox Code Playgroud)
演示.请注意,GCC查找被破坏,因为它不考虑typename T::beginin的非类型名称TestMemType::test_end/begin.可在此处找到变通方案草图.
第2步中的检查要求类类型是可派生的,这意味着此方法不适用于final类或联合 - 如果它们具有名称为begin/ 的不可访问成员end.
| 归档时间: |
|
| 查看次数: |
665 次 |
| 最近记录: |