web-dev-qa-db-fra.com

Puis-je écrire cette instruction if avec une déclaration de variable sur une ligne?

Je me demandais s'il y avait un moyen de mettre cela sur une seule ligne?

if (auto r = getGlobalObjectByName(Word)) r->doSomething; // This works fine

if (!auto r = getGlobalObjectByName(Word)) r->doSomething; // Says "expected an expression"

if (auto r = getGlobalObjectByName(Word) == false) r->doSomething; // Also doesn't work.

J'ai également essayé de l'entourer de supports supplémentaires, mais cela ne semble pas fonctionner. Je trouve cela très pratique de le faire sur une seule ligne.

26
Zebrafish

Depuis C++ 17, vous pouvez utiliser un initializer if-statement :

if (auto r = getGlobalObjectByName(Word); !r) r->doSomething;

La sémantique est:

if (init-statement; condition) statement

La seule différence avec l'instruction if "traditionnelle" est le init-statement, qui initialise une variable dans la portée du bloc, similaire aux boucles for.

49
Passer By

Si vous avez C++ 17, utilisez le formulaire if (init statement; condition). Sinon, vous avez trois choix:

  • Arrêtez d'essayer de garder tout cela sur une seule ligne. Par exemple:

    auto r = getGlobalObjectByName(Word);
    if (!r) r->doSomething();
    
  • Utilisez un else:

    if (auto r = getGlobalObjectByName(Word)) {} else r->doSomething();
    

(Notez que cela nécessite que r soit un pointeur intelligent avec très une sémantique étrange pour la fonction operator bool(). OTOH, je suppose que c'est en fait un court exemple) plutôt que votre code actuel).

Je pense que je n'utiliserais la forme else que s'il était vraiment important de tout garder sur une seule ligne (pour conserver une mise en forme tabulaire du code par exemple).

12
Martin Bonner

Ce que vous essayez de faire est bien. En définissant la variable à l'intérieur de si vous restreignez sa portée . Cela permet de réduire le nombre de variables parasites une fois qu'elles ont atteint leur objectif.

En utilisant cette technique, si vous souhaitez suivre le chemin négatif , vous devez utiliser else comme ceci:

if(auto r = getGlobalObjectByName(Word))
{
    r->doSomething();
}
else
{
    // r == nullptr
    // so do something else
}
4
Galik

Il existe également un moyen de le faire avec lambdas et C++ 14, mais cela semble plutôt idiot.

[](auto r){ if(!r)r->doSomething(); }(getGlobalObjectByName(Word));

En C++ 11, vous pouvez également faire ce désordre horrible (même idée, juste pas de auto)

[](decltype(getGlobalObjectByName(Word)) r){ if(!r)r->doSomething(); }(getGlobalObjectByName(Word));

Ce n'est certainement pas mieux que cette version plus claire de C++ 11 mentionnée par Martin Bonner :

{
    auto r = getGlobalObjectByName(Word);
    if(!r)r->doSomething();
}

Ici, votre code indique clairement que vous voulez que r ne soit présent que pendant la durée de l'instruction if.

4
Erroneous

Avant C++ 17, vous pouvez définir une classe wrapper, comme ici:

#include <utility>
template<typename T>
class NotT
{
    T t;
public:
    template<typename U>
    NotT(U&& u) : t(std::move(u)) {}
    explicit operator bool() const { return !t; }
    T      & value()       { return t; }
    T const& value() const { return t; }
};
template<typename T> NotT<T> Not(T&& t)
{
    return NotT<T>(std::move(t));
}

#include <memory>
#include <iostream>

int main()
{
    if(auto p=Not(std::make_shared<int>(2134)))
        std::cout << "!p: p=" << p.value().get() << '\n';
    else
        std::cout << "!!p: p=" << p.value().get() << ", *p=" << *p.value() << '\n';

}

Live example

2
Ruslan