【问题标题】:How can one make a variadic-based chained-indexing function?如何制作基于可变参数的链式索引函数?
【发布时间】:2012-04-16 09:20:44
【问题描述】:

如果我有一个对象a 是一个内置数组或具有合适operator [] 的类类型,并且它的返回类型可以自己索引,我应该如何编写一个可以索引所有的通用函数其中有一个可变参数调用而不是分隔的括号块?换句话说,我可以表达如下:

a[i0][i1]...[iK]

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

slice( a, i0, i1, ..., iK )

因为 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)... );
}

ce_forward 函数模板是一个constexpr 标记的std::forward。 (标准中可以标记为constexpr 的一些东西不是。)我试图找出正确的东西来放入返回类型和异常规范点。我知道的一些案例和注意事项是:

  • 内置operator [] 要求一个操作数是数据指针(或衰减数组引用),另一个是枚举或整数类型。操作数可以是任意顺序。我不知道是否可以将操作数替换为具有明确(非显式?)转换为适当类型的类类型。
  • 类类型可以将operator [] 定义为非静态成员函数。任何此类函数只能有一个参数(this 除外)。
  • 内置运算符不能抛出。 (除非操作数可以基于 UDT 转换;所述转换是唯一可能的抛出点。)由边界错误引起的未定义行为超出了我们的权限范围。
  • 最后一次索引调用返回左值引用、右值引用还是值应该反映在slice的返回类型中。
  • 如果(最终?)调用是按值调用,则应将std::is_nothrow_move_constructible&lt;ReturnType&gt;::value 与异常规范进行“或”运算。 (按引用返回是noexcept。)
  • 如果内置运算符涉及数组,则该步骤的返回引用应该是 r 值引用(如果数组也是 1)。 (这是一种缺陷,因为与数组不同,指针会丢失其目标的左值与右值状态,因此传统的返回始终是左值引用。)
  • 对于类类型索引运算符,请确保异常规范和返回类型部分引用函数体使用的相同重载(如果有多个)。请注意仅在 this 的限定条件上有所不同的重载(const/volatile/both/neither 和/或 &amp;/&amp;&amp;/neither)。

【问题讨论】:

  • 对于我使用它的目的,它应该尽可能是constexpr
  • 您是否考虑过简单地将与return 语句中相同的表达式用于返回类型和noexcept 规范?例如。 noexcept(noexcept(std::declval&lt;T&gt;()[std::declval&lt;U&gt;()])) -&gt; decltype(std::declval&lt;T&gt;()[std::declval&lt;U&gt;()]).

标签: c++ arrays c++11


【解决方案1】:

根据@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)... );
}

我将full example program 设置为 Gist。它适用于运行 GCC >= 4.7、CLang >= 3.2 和 Intel C++ >= 13.0 的网站编译器。

【讨论】:

  • bool direct_can_throw = not noexcept(...)not 应该在那里吗?
  • @0x499602D2,是的。 noexcept 的东西想知道我们什么时候投掷,但我的班级计算什么时候投掷。
  • 哦,是的,我忘记了 not 关键字与 ! 相同 :)
【解决方案2】:

要为slice 函数获取正确的返回值,您可以创建一个帮助模板来计算正确的结果类型。例如:

template <typename T, typename U, typename... V>
struct SliceResult {
    typedef typename SliceResult<T, V...>::Type Type;
};

template <typename T, typename U>
struct SliceResult<T, U> {
    typedef typename std::underlying_type<T[U(0)]>::type Type;
};

slice 函数随后将返回 SliceResult&lt;...&gt;::Type

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-15
    • 1970-01-01
    • 1970-01-01
    • 2020-06-30
    • 1970-01-01
    相关资源
    最近更新 更多