web-dev-qa-db-fra.com

Pourquoi et quand l'opérateur ternaire renvoie-t-il une valeur l?

Pendant longtemps, j'ai pensé que l'opérateur ternaire renvoie toujours une valeur r. Mais à ma grande surprise, ce n'est pas le cas. Dans le code suivant, je ne vois pas la différence entre la valeur de retour de foo et la valeur de retour de l'opérateur ternaire.

#include <iostream>
int g = 20 ;

int foo()
{
    return g ;
}

int main()
{
    int i= 2,j =10 ;

    foo()=10 ; // not Ok 
    ((i < 3) ? i : j) = 7; //Ok
    std::cout << i <<","<<j << "," <<g << std::endl ;
}
39
Soulimane Mammar

i et j sont glvalues ​​ (voir cette référence de catégorie de valeur pour plus de détails) .

Alors si vous lisez cette référence d'opérateur conditionnel nous arrivons à ce point:

4) Si E2 et E3 sont des valeurs gl du même type et de la même catégorie de valeur, alors le résultat a le même type et la même catégorie de valeur

Donc, le résultat de (i < 3) ? i : j est une valeur gl qui peut être affectée à.

Cependant, faire quelque chose comme ça n'est vraiment pas quelque chose que je recommanderais.

35
Some programmer dude

Les règles pour cela sont détaillées dans [expr.cond] . Il existe de nombreuses branches pour plusieurs combinaisons de types et de catégories de valeurs. Mais finalement, l'expression est une valeur dans le cas par défaut. Le cas dans votre exemple est couvert par le paragraphe 5:

Si les deuxième et troisième opérandes sont des valeurs gl de la même catégorie de valeur et ont le même type, le résultat est de ce type et de cette catégorie de valeur et il s'agit d'un champ binaire si le deuxième ou le troisième opérande est un champ binaire, ou si les deux sont des champs binaires.

i et j, étant des noms de variables, sont des expressions de valeur de type int. L'opérateur conditionnel produit donc une int lvalue.

L'opérateur conditionnel ternaire donnera une valeur l, si le type de ses deuxième et troisième opérandes est une valeur l.

Vous pouvez utiliser le modèle de fonction is_lvalue (ci-dessous) pour savoir si un opérande est une valeur l et l'utiliser dans le modèle de fonction isTernaryAssignable pour savoir s'il peut être affecté à.

Un exemple minimal:

#include <iostream>
#include <type_traits>

template <typename T>
constexpr bool is_lvalue(T&&) {
  return std::is_lvalue_reference<T>{};
}

template <typename T, typename U>
bool isTernaryAssignable(T&& t, U&& u)
{
    return is_lvalue(std::forward<T>(t)) && is_lvalue(std::forward<U>(u));
}

int main(){
    int i= 2,j =10 ;

    ((i < 3) ? i : j) = 7; //Ok

    std::cout << std::boolalpha << isTernaryAssignable(i, j); std::cout << '\n';
    std::cout << std::boolalpha << isTernaryAssignable(i, 10); std::cout << '\n';
    std::cout << std::boolalpha << isTernaryAssignable(2, j); std::cout << '\n';
    std::cout << std::boolalpha << isTernaryAssignable(2, 10); std::cout << '\n';   
}

Production:

true
false
false
false

DÉMO EN DIRECT

Remarque : Les opérandes que vous passez à isTernaryAssignable doivent être tels qu'ils ne subiront pas de décroissance (par exemple, un tableau qui se désintègre pour pointer ).

2
P.W