Si j’ai un object qui soit un tableau intégré ou un type de classe avec un operator []
approprié operator []
, et son type retourné peut être indexé lui-même, comment devrais-je écrire une fonction générique qui peut tous les indexer avec une variable appeler au lieu de bloc de support séparé? En d’autres termes, je peux faire des expressions comme:
a[i0][i1]...[iK]
et je veux pouvoir l’écrire comme une seule fonction:
slice( a, i0, i1, ..., iK )
puisque les règles de C ++ exigent que l’ operator []
travaille sur des arguments simples, ce qui le rend moins compatible avec les éléments variadiques. (Cette question est basée sur un fil Usenet dans lequel j’ai essayé de poser une question similaire; je n’ai finalement résolu que moi-même les tableaux intégrés éventuellement nesteds.)
Un premier coup de poignard:
template constexpr auto slice( T &&t, U &&u ) noexcept(???) -> ??? { return ce_forward(t)[ ce_forward(u) ]; } template constexpr auto slice( T &&t, U &&u, V &&v, W... &&w ) noexcept(???) -> ??? { return slice( ce_forward(t)[ce_forward(u)], ce_forward(v), ce_forward(w)... ); }
Les ce_forward
fonction ce_forward
sont std::forward
constmarkés. (Certains éléments de la norme qui pourraient être marqués constexpr
ne le sont pas.) J’essaie de trouver le bon matériel à insérer dans les zones de type de retour et de spécification d’exception. Certains cas et mises en garde que je connais sont les suivants:
operator []
intégré operator []
nécessite qu’un opérande soit un pointeur de données (ou une référence de tableau décomposé) et l’autre un type énumération ou entier. Les opérandes peuvent être dans l’un ou l’autre ordre. Je ne sais pas si un opérande pourrait être remplacé par un type de classe avec une conversion non ambiguë (non explicite?) En un type approprié. operator []
tant que fonction (s) membre non statique. Une telle fonction ne peut avoir qu’un seul paramètre (à part this
). slice
. std::is_nothrow_move_constructible::value
doit être OUI pour la spécification de l’exception. (Les retours par référence sont noexcept
.) this
( const
/ volatile
/ both / ni et / ou &
/ &
&&
/ ni). J’ai mis au point une solution basée sur le commentaire de @ luc-danton et sur la réponse de @ user315052.
#include template < typename Base, typename ...Indices > class indexing_result; template < typename T > class indexing_result { public: using type = T; static constexpr bool can_throw = false; }; template < typename T, typename U, typename ...V > class indexing_result { using direct_type = decltype( std::declval ()[std::declval()] ); using next_type = indexing_result; static constexpr bool direct_can_throw = not noexcept( std::declval()[std::declval()] ); 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); } template < typename T, typename U, typename ...V > inline constexpr auto slice( T &&t, U &&u, V &&...v ) noexcept( !indexing_result::can_throw ) -> typename indexing_result::type { return slice( static_cast (t)[static_cast( u )], static_cast(v)... ); }
Je mets un exemple complet de programme en tant que Gist. Cela fonctionnait sur un compilateur de site Web exécutant GCC> = 4.7, CLang> = 3.2 et Intel C ++> = 13.0.
Pour obtenir la bonne valeur de retour pour la fonction slice
, vous pouvez créer un modèle d’assistance qui calcule le type de résultat correct. Par exemple:
template struct SliceResult { typedef typename SliceResult::Type Type; }; template struct SliceResult { typedef typename std::underlying_type::type Type; };
La fonction slice
renverrait alors un SliceResult<...>::Type
.