如何制作基于可变参数的链式索引函数?

CTM*_*ser 5 c++ arrays c++11

如果我有一个对象a,无论是内置数组还是具有合适类的类型operator [],并且其返回类型都可以自己编入索引,我应该如何编写一个泛型函数,该函数可以使用可变参数调用而不是分离来索引所有这些函数支架块?换句话说,我可以制作如下表达式:

a[i0][i1]...[iK]
Run Code Online (Sandbox Code Playgroud)

我希望能够将其作为单个函数编写:

slice( a, i0, i1, ..., iK )
Run Code Online (Sandbox Code Playgroud)

因为C++的规则需要operator []处理单个参数,使其与可变参数的东西不太兼容.(这个问题基于一个Usenet线程,在那里我尝试了类似的东西;最后我自己只解决了可能嵌套的内置数组.)

第一次刺:

template < typename T, typename U >
constexpr
auto slice( T &&t, U &&u ) noexcept(???) -> ???
{ return ce_forward<T>(t)[ ce_forward<U>(u) ]; }

template < typename T, typename U, typename V, typename ...W >
constexpr
auto slice( T &&t, U &&u, V &&v, W... &&w ) noexcept(???) -> ???
{
    return slice( ce_forward<T>(t)[ce_forward<U>(u)], ce_forward<V>(v),
     ce_forward<W>(w)... );
}
Run Code Online (Sandbox Code Playgroud)

ce_forward函数模板是一个constexpr-marked std::forward.(标准中可以标记的一些东西constexpr不是.)我正在试图找出正确的东西放入返回类型和异常规范点.我知道的一些案例和警告是:

  • 内置operator []需要一个操作数作为数据指针(或衰减数组引用),另一个操作数是枚举或整数类型.操作数可以按任意顺序排列.我不知道操作数是否可以用具有明确(非显式?)转换为适当类型的类类型替换.
  • 类类型可以定义operator []为非静态成员函数.任何这样的函数只能有一个参数(除此之外this).
  • 内置运算符不能抛出.(除非操作数可以基于UDT转换;所述转换是唯一可能的抛出点.)由边界错误引起的未定义行为超出了我们的范围.
  • 最后一个索引调用是返回l值引用,r值引用还是值应该反映在返回类型中slice.
  • 如果(final?)调用是by-value,那么std::is_nothrow_move_constructible<ReturnType>::value应该对异常规范进行OR运算.(按参考回报是noexcept.)
  • 如果内置运算符涉及数组,则该步骤的返回引用应该是r值引用(如果数组也是一个).(这是一种缺陷,因为与数组不同,指针会失去目标的l-与r值状态,因此传统的返回始终是l值引用.)
  • 对于类类型索引运算符,请确保exception-spec和return-type节引用函数体使用的相同重载(如果不止一个).警惕仅在this(const/ volatile/ both/both和/或&/ &&/ both)的资格不同的重载.

CTM*_*ser 3

我根据@luc-danton 的评论和@user315052 的回答提出了一个解决方案。

#include <utility>

template < typename Base, typename ...Indices >
class indexing_result;

template < typename T >
class indexing_result<T>
{
public:
    using type = T;

    static constexpr
    bool  can_throw = false;
};

template < typename T, typename U, typename ...V >
class indexing_result<T, U, V...>
{
    using direct_type = decltype( std::declval<T>()[std::declval<U>()] );
    using   next_type = indexing_result<direct_type, V...>;

    static constexpr
    bool  direct_can_throw
     = not noexcept( std::declval<T>()[std::declval<U>()] );

public:
    using type = typename next_type::type;

    static constexpr
    bool  can_throw = direct_can_throw || next_type::can_throw;
};

template < typename T >
inline constexpr
auto  slice( T &&t ) noexcept -> T &&
{ return static_cast<T &&>(t); }

template < typename T, typename U, typename ...V >
inline constexpr
auto  slice( T &&t, U &&u, V &&...v )
  noexcept( !indexing_result<T, U, V...>::can_throw )
  -> typename indexing_result<T, U, V...>::type
{
    return slice( static_cast<T &&>(t)[static_cast<U &&>( u )],
     static_cast<V &&>(v)... );
}
Run Code Online (Sandbox Code Playgroud)

我把一个完整的示例程序作为要点。它可以在运行 GCC >= 4.7、CLang >= 3.2 和 Intel C++ >= 13.0 的网站编译器上运行。