Вопрос по c++11, arrays, c++ – Как можно создать цепочечно-индексную функцию на основе переменных?

5

Если у меня есть объектa что либо встроенный массив или тип класса с подходящимoperator []и его возвращаемый тип может быть проиндексирован сам, как мне написать обобщенную функцию, которая может индексировать все из них с помощью запроса с переменным числом вместо разделенного блока скобок? Другими словами, я могу сделать такие выражения:

<code>a[i0][i1]...[iK]
</code>

и я хочу иметь возможность написать это как одну функцию:

<code>slice( a, i0, i1, ..., iK )
</code>

поскольку правила C ++ требуютoperator [] работать над отдельными аргументами, делая его менее совместимым с разнообразными вещами. (Этот вопрос основан на потоке Usenet, где я пытался задать что-то похожее; в конечном итоге я решил самостоятельно решать только встроенные массивы, возможно вложенные в них.)

Первый удар:

<code>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)... );
}
</code>

ce_forward шаблоны функций являютсяconstexprзвёздочкаstd::forward, (Некоторые вещи в стандарте, которые могут быть отмеченыconstexpr are 't.) Я пытаюсь выяснить, что нужно поместить в области спецификации типа возврата и исключения. Вот некоторые известные мне случаи и предостережения:

The built-in operator [] requires one operand to be a data-pointer (or decayed array reference) and the other to be an enumeration or integer type. The operands can be in either order. I don't know if an operand could be replaced with a class-type with an unambiguous (non-explicit?) conversion to an appropriate type. A class type can define operator [] as non-static member function(s). Any such function can only have one parameter (besides this). The built-in operator can't throw. (Unless operands could be based on an UDT conversion; said conversion is the only possible throw point.) The undefined behavior caused by boundary errors is beyond our purview here. Whether the last indexing call returns a l-value reference, r-value reference, or value should be reflected in the return type of slice. If the (final?) call is by-value, then std::is_nothrow_move_constructible<ReturnType>::value should be OR'd to the exception specification. (By-reference returns are noexcept.) If the built-in operator involves an array, the returned reference for that step should be a r-value reference if the array is one too. (This is kind-of a defect because pointers, unlike arrays, lose their target's l- vs. r-value status, so the traditional return would always be a l-value reference.) For class-type indexing operators, make sure the exception-spec and return-type sections refer to the same overload (if more than one) that the function body uses. Be wary for overloads that differ only in the qualification of this (const/volatile/both/neither and/or &/&&/neither).
Это должно бытьconstexpr? kennytm
Рассматривали ли вы просто положить то же выражение, что и вreturnператор @ для типа возвращаемого значения иnoexcept Спецификация? Например.noexcept(noexcept(std::declval<T>()[std::declval<U>()])) -> decltype(std::declval<T>()[std::declval<U>()]). Luc Danton
Для чего я его использую, должно бытьconstexpr когда возможно CTMacUser

Ваш Ответ

2   ответа
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)... );
}

Я положил полный пример программы как гист. Он работал на веб-компиляторе, работающем под управлением GCC> = 4.7, CLang> = 3.2 и Intel C ++> = 13.0.

bool direct_can_throw = not noexcept(...) являетсяnot должен быть там? 0x499602D2
О да, я забылnot ключевое слово было таким же, как!:) 0x499602D2
@ 0x499602D2, да.noexcept Вещи хотят знать, когда мы делаемн бросить, но мой класс вычисляет, когда бросить. CTMacUser
1

Чтобы получить правильное возвращаемое значение для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;
};

Thesliceункция @ тогда вернула быSliceResult<...>::Type.

Похожие вопросы