Lors de la restructuration du code, je suis tombé sur un "problème" lors du retour d'une structure avec 2 valeurs. Maintenant, ceux-ci devraient vraiment être nommés pour l'effet documenté. Plus tard, j'ai voulu utiliser tie, alors j'ai changé la structure en héritant de std::pair et en définissant simplement des références. Maintenant, cela fonctionne très bien, mais vous remarquerez maintenant que ma structure a la taille de 24 au lieu de seulement 8 par rapport à la paire.

#include <tuple>


struct Transaction : public std::pair<int, int> {
    using pair::pair;

  int& deducted = first;
  int& transfered = second;
};
//static_assert(sizeof(Transaction) == sizeof(std::pair<int, int>));//commenting in this line will fail compilation

Transaction makeTheTransaction();

void test(int& deduct, int& transfer) {
    std::tie(deduct, transfer) = makeTheTransaction(); 
}

La méthode peut-être évidente est de changer en fonctions membres, mais c'est aussi trop "passe-partout" pour ce cas (alors il devient plus facile de ne pas utiliser tie plus tard). Un memcpy direct est par exemple. un tuple est droit vers l'avant UB. Une liaison structurée directe n'est pas non plus faisable puisque les variables sont déjà utilisées.

Ma question est de savoir quelle est une solution de code meilleure ou minimale sans tenir compte des pièces réutilisables (et étant donné que la taille ne devrait pas dépasser la taille de 2 pouces)?

MISE À JOUR: Pour ce cas, j'ai fini par simplement faire une structure simple et maintenir le retour dans un temporaire. Pour les autres utilisateurs qui viennent ici, il existe une proposition de bibliothèque pour booster qui semble être capable de convertir n'importe quelle structure en tuple: https://github.com/apolukhin/magic_get/

1
darune 20 nov. 2018 à 22:19

3 réponses

Meilleure réponse

Il me semble que vous me compliquez trop le problème. Si vous devez utiliser std::tie, vous pouvez simplement l'utiliser. Il n'est pas nécessaire de modifier votre structure:

struct Transaction
{
  int deducted;
  int transferred;
};

// later...

auto t = std::tie(transaction.deducted, transaction.transferred);

S'il s'agit d'un modèle que vous utilisez fréquemment, vous pouvez l'envelopper dans une petite méthode d'aide:

struct Transaction
{
  int deducted;
  int transferred;

  auto to_tuple() const
  {
    return std::tie(deducted, transferred);
  }
};

Vous pouvez également l'utiliser pour affecter plusieurs variables à la fois, bien que je déconseille fortement cela. Il est sujet aux erreurs et conduit à un code fragile. (Par exemple, si vous inversez l'ordre de deduct et transfer dans l'exemple ci-dessous, vous avez un bogue, mais le compilateur ne donnera aucun avertissement ou erreur.)

void test(int& deduct, int& transfer)
{
  std::tie(deduct, transfer) = makeTheTransaction().to_tuple();
}

Modifier: après réflexion ...

Si l'objectif ici est simplement une décomposition facile de la structure en variables, vous pouvez le faire directement et éviter d'utiliser des paires ou des tuples:

struct Transaction
{
  int deducted;
  int transferred;

  void decompose(int* deducted_, int* transferred_)
  {
    *deducted_ = deducted;
    *transferred_ = transferred;
  }
};

void test(int& deduct, int& transfer)
{
  makeTheTransaction().decompose(&deduct, &transfer);
}

C'est encore fragile, mais au moins maintenant, vous obtiendrez une intelligence lorsque vous écrivez l'appel à la méthode decompose, ce qui rendra le motif un peu moins sujet aux erreurs.

1
Peter Ruderman 20 nov. 2018 à 20:26

L'ajout d'une fonction de conversion fonctionne:

#include <tuple>

struct Transaction {
    std::pair<int, int> data_;

    operator std::tuple<int &, int &> () {
        return std::tie(data_.first, data_.second);
    }
};
static_assert(sizeof(Transaction) == sizeof(std::pair<int, int>));

Transaction makeTheTransaction() {
    return Transaction();
}

void test(int& deduct, int& transfer) {
    std::tie(deduct, transfer) = makeTheTransaction();
}

Je ne pense pas que cela causera un problème à vie lors de l'utilisation avec std :: tie.

Ce Transaction ne fonctionne pas avec une liaison structurée avec deux identifiants. Mais vous pouvez faire en sorte qu'il prenne en charge la liaison «de type tuple» en lui spécialisant std::get et std::tuple_size.

0
felix 20 nov. 2018 à 19:56

J'irais simplement avec:

struct Transaction
{
    int deducted;
    int transfered;
};

Avec une utilisation similaire à:

Transaction makeTheTransaction() { return {4, 2}; }

int main()
{
    auto [deduct, transfer] = makeTheTransaction(); 
    std::cout << deduct << transfer << std::endl;
}

Démo

0
Jarod42 20 nov. 2018 à 19:29