web-dev-qa-db-fra.com

Quelle est la raison de `std :: make_Tuple`?

Je veux dire pourquoi std::make_Tuple existe? Je sais qu'il existe des situations où la fonction réduit la quantité de caractères que vous devez taper car vous pouvez éviter les paramètres de modèle. Mais est-ce la seule raison? Ce qui rend std::Tuple spécial que la fonction existe alors que d'autres modèles de classe n'ont pas une telle fonction? Est-ce uniquement parce que vous pouvez utiliser std::Tuple plus souvent dans de telles situations?


Voici deux exemples où std::make_Tuple réduit le nombre de caractères:

// Avoiding template parameters in definition of variable.
// Consider that template parameters can be very long sometimes.
std::Tuple<int, double> t(0, 0.0); // without std::make_Tuple
auto t = std::make_Tuple(0, 0.0);  // with std::make_Tuple

// Avoiding template parameters at construction.
f(std::Tuple<int, double>(0, 0.0)); // without std::make_Tuple
f(std::make_Tuple(0, 0.0));         // with std::make_Tuple

Mais comme écrit ci-dessus, vous n'avez pas de fonction comme celle-ci pour de nombreux autres modèles de classe.

26
JojOatXGME

Parce que vous ne pouvez pas utiliser la déduction d'argument pour les constructeurs. Vous devez écrire explicitement std::Tuple<int, double>(i,d);.

Cela le rend plus pratique pour créer un tuple et le passer à une autre fonction en une seule fois.

takes_Tuple(make_Tuple(i,d)) vs takes_Tuple(tuple<int,double>(i,d)).

Un endroit de moins à changer lorsque le type de i ou d change, surtout s'il y avait des conversions possibles entre l'ancien et le nouveau type.

S'il était possible d'écrire std::Tuple(i,d);, make_* Serait ( probablement) redondant.

(Ne demandez pas pourquoi ici. Peut-être pour des raisons similaires pourquoi la syntaxe A a(); n'invoque pas un constructeur par défaut. Il y a quelques particularités douloureuses de la syntaxe c ++.)

NOTE DE MISE À JOUR: Comme Daniel le remarque à juste titre, c ++ 17 sera amélioré, de sorte que la déduction des arguments de modèle fonctionnera pour les constructeurs , et cette délégation deviendra obsolète.

25
luk32

Nous pouvons trouver une raison pour laquelle nous avons besoin de make_Tuple et les divers autres utilitaires make_ * de la proposition N3602: Déduction de paramètres de modèle pour les constructeurs qui dit ( c'est moi qui souligne):

Cet article propose d'étendre la déduction des paramètres de modèle pour les fonctions aux constructeurs de classes de modèle. La façon la plus claire de décrire le problème et sa solution est de donner quelques exemples.

Supposons que nous ayons défini ce qui suit.

vector<int> vi1 = { 0, 1, 1, 2, 3, 5, 8 }; 
vector<int> vi2; template<class Func> 
    class Foo() { 
        public: Foo(Func f) : func(f) {} 
        void operator()(int i) { os << "Calling with " << i << endl; f(i); } 
        private: 
        Func func;
    };

Actuellement, si nous voulons instancier des classes de modèle, nous devons spécifier les paramètres du modèle ou utiliser un wrapper "make_ *", tirer parti de la déduction des paramètres du modèle pour les fonctions , ou punt complètement:

pair<int, double> p(2, 4.5); 
auto t = make_Tuple(4, 3, 2.5); 
copy_n(vi1, 3, back_inserter(vi2)); // Virtually impossible to pass a lambda to a template class' constructor
for_each(vi.begin(), vi.end(), Foo<???>([&](int i) { ...}));

Remarque, la proposition est suivie via EWG issue 6 .

8
Shafik Yaghmour

Uniquement pour la déduction d'argument modèle. Cependant, voici un exemple (artificiel) où cela est requis pour utiliser un lambda:

class A
{
public:
    template<typename F>
    A(const std::Tuple<F> &t)
    {
        // e.g.
        std::get<0>(t)();
    }
};

class B : public A
{
public:
     B(int i) : A(std::make_Tuple([&i]{ ++i; }))
     {
         // Do something with i
     }
};

std::Tuple<decltype([&i]{ ++i; })>([&i]{ ++i; }) ne peut pas être utilisé car les deux expressions lambda ont des types différents. Un wrapper polymorphe comme std::function Ajoute une surcharge d'exécution. Une classe nommée avec operator () définie par l'utilisateur fonctionnerait (qui pourrait également devoir être un ami de B, selon le contenu du corps de l'opérateur). C'est ce que nous utilisions jadis avant C++ 11.

1
Arne Vogel

Je pense qu'une utilisation intelligente de ce type de fonction doit être passée en paramètre.
Quelque chose comme ca:

std::bind_front(&std::make_Tuple, 1, "test", true);

Cela pourrait être utile car, si je ne me trompe pas, nous ne pouvons pas appeler directement les constructeurs.

auto obj = Object::Object(3); // error: cannot call constructor ‘Object::Object’ directly [-fpermissive]
1
David Gallay