40 votes

Pourquoi n'y a-t-il pas de construction de tuple par morceaux?

Les modèles standard std::pair et std::array sont des cas particuliers de std::tuple, et il va de soi qu'ils doivent avoir une très similaires ensemble de capacités.

Cependant, unique parmi les trois, std::pair permet par morceaux de la construction. C'est, si les types T1 et T2 peut être construit à partir d'un ensemble d'arguments a1, a2, ... et b1, b2, ..., puis moralement parlant, nous pouvons faire une paire

"pair<T1, T2> p(a1, a2, ..., b1, b2, ...)"

directement. Pratiquement, cela est détaillé comme quelque chose comme ceci:

std::pair<T1, T2> p(std::piecewise_construct,
                    std::forward_as_tuple(a1, a2, ...),
                    std::forward_as_tuple(b1, b2, ...));

Question: Pourquoi n'est pas le même par morceaux constructibility existent pour les tableaux et les tuples? Est-il une raison plus profonde, ou est-ce une simple omission? Par exemple, il serait agréable d'avoir:

std::tuple<T1, T2, T3> t(std::piecewise_construct,
                         std::forward_as_tuple(a1, a2, ...),
                         std::forward_as_tuple(b1, b2, ...),
                         std::forward_as_tuple(c1, c2, ...));

Est-il une raison pour laquelle cela ne peut être fait? [Edit: Ou suis-je mal comprendre le but de piecewise de construction entièrement?]

(Je ne vraiment avoir une situation dans laquelle je voudrais initialiser un vecteur de n-uplets avec un défaut la valeur de l'élément que je préfère construire directement à partir des arguments, sans fautes d'orthographe de chaque tuple de type d'élément nouveau.)

16voto

Jonathan Wakely Points 45593

Question: Pourquoi n'est pas le même par morceaux constructibility existent pour les tableaux et les tuples?

Mon souvenir est que par morceaux de la construction a été ajouté à l' std::pair pour une seule raison: le soutien utilise-allocateur de la construction de la paire d'éléments, c'est à dire de permettre à un allocateur être fournis et le conditionnel passé pour les éléments s'ils prennent en charge la construction avec un allocateur (voir [allocateur.utilise] dans la norme).

Au cours de C++0x processus d' std::pair avaient deux fois plus nombreux constructeurs comme il le fait maintenant, avec chaque constructeur ayant un correspondant "de l'allocateur-étendue" version prenant un std::allocator_arg_t et un allocateur de l'argument par exemple

template<class T, class U>
  struct pair {
    pair();
    pair(allocator_arg_t, const Alloc&);
    template<class TT, class UU>
      pair(TT&&, UU&&);
    template<class Alloc, class TT, class UU>
      pair(allocator_arg_t, const Alloc&, TT&&, UU&&);
    // etc.

Il y avait quelque chose une blague (haha, sérieux seulement) sur le fou de la complexité de l' std::pair. Le soutien pour le passage des allocateurs pour les éléments a été retiré de std::pair et s'est déplacée en std::scoped_allocator_adaptor, qui est responsable de la détection de savoir si les éléments doivent être construits avec un allocateur (voir l' construct des surcharges de prendre un pointeur vers std::pair dans [l'allocateur.l'adaptateur.membres]).

Une belle conséquence de la par morceaux de la construction est que vous pouvez faire "emplace style" initialisation de la paire d'éléments, permettant de paires de non-déplaçable, non-copiable types, mais pour autant que je sais que c'est pas le but de la conception.

Donc, la raison tuple ne supporte pas c'est que la fonctionnalité a été inventé pour simplifier pair qui avait explosé, passant d'un type très simple en C++03 à une risée dans C++0x, mais en faisant de même pour tuple n'était pas considéré comme important (c'était nouveau pour le C++11 de toute façon). Aussi, s'étendant scoped_allocator_adaptor pour gérer les tuples d'un nombre arbitraire d'éléments ont fait que l'adaptateur beaucoup plus compliqué.

Comme pour std::array, c'est un type de regroupement (parce que les raisons de l'ajout d'un constructeur prenant piecewise_construct_t n'est pas possible sans un non agrégées.

13voto

Dave S Points 11381

Je ne sais pas pourquoi il n'y est pas. Auparavant, je pensais que la mise en œuvre ne serait pas possible, compte tenu de l'actuelle varadic la syntaxe du modèle, mais j'ai réalisé que cela peut être fait si c'est cassé en morceaux.

Si ils ont défini une interface comme ceci:

template<typename... T>
tuple(piecewise_construct, T&&... t);

Et en fait une exigence que les arguments sont quelque chose que vous pouvez utiliser std::get<N> pour accéder à la arguments (en gros, les tuples, les paires, les tableaux). Il y aurait à être contrôles supplémentaires afin de vérifier qu'il n'existe pas d'incompatibilité entre le nombre d'arguments donnés et le nombre d'éléments dans le tuple.

Edit: Ce problème a été me tracasse depuis que j'ai lu il. Et j'ai créé la classe suivante, il est dérivé de la std::tuple, et n'a pas de membres de données, de sorte que vous pouvez l'affecter à la n-uplet et le découpage est inoffensif. La version actuelle exige que les éléments soient mobiliers ou copiable, car elle crée un temporaire, puis les insère dans le tuple. Si vous étiez un tuple maître d'oeuvre, il devrait être possible d'éliminer même de ce mouvement.

namespace detail
{
template<int ... N>
struct index {
    typedef index<N..., sizeof...(N)> next;
};
template<int N>
struct build_index {
    typedef typename build_index<N - 1>::type::next type;
};

template<>
struct build_index<0> {
    typedef index<> type;
};

template<typename T>
struct tuple_index {
    typedef typename build_index<
            std::tuple_size<typename std::remove_reference<T>::type>::value>::type type;

};
}
template<typename ... Elements>
class piecewise_tuple: public std::tuple<Elements...>
{
    typedef std::tuple<Elements...> base_type;

    template<int Index, typename ... Args, int ... N>
    static typename std::tuple_element<Index, base_type>::type 
    construct(std::tuple<Args...>&& args, detail::index<N...>)
    {
        typedef typename std::tuple_element<Index, base_type>::type result_type;
        return result_type(std::get<N>(std::move(args))...);
    }

    template<int ...N, typename ArgTuple>
    piecewise_tuple(detail::index<N...>, ArgTuple&& element_args)
    : base_type( construct<N>( std::get<N>(std::forward<ArgTuple>(element_args)),
                 typename detail::tuple_index< typename std::tuple_element<N, typename std::remove_reference<ArgTuple>::type >::type >::type() )...)
    {

    }

public:

    piecewise_tuple() = default;

    // For non-piecewise constructors, forward them
    template<typename... Args>
    piecewise_tuple(Args&&... args) : base_type(std::forward<Args>(args)...) {}


    template<typename... T>
    piecewise_tuple(std::piecewise_construct_t, T&&... args) :
    piecewise_tuple(typename detail::tuple_index<base_type>::type(),    
                    std::forward_as_tuple(std::forward<T>(args)...))
    {

    }


};

// Usage example
int main()
{
   int i = 5;
   std::unique_ptr<int> up(new int(0));

   piecewise_tuple<std::pair<int, int>, double, std::unique_ptr<int>, int& >
   p(std::piecewise_construct,
    std::forward_as_tuple(1,2),
    std::forward_as_tuple(4.3),
    std::forward_as_tuple(std::move(up)),
    std::forward_as_tuple(i));
   return 0;
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X