Les opérateurs associatifs du C++ sont asociaux

Le C++ a le défaut d’être un peu binaire… dans sa façon de traiter les opérations n-aires qui se moque bien de l’intention associative du programmeur. Faut-il attendre un compilateur quantique avec états non-binaires pour traiter ensemble les opérandes associées par les parenthèses? 😉

Le problème

Bien que les opérateurs + et * soient associatifs du point de vue mathématique, le C++ traite leurs opérandes 2 par 2 de gauche à droite. Ainsi a+b+c+d est vu comme: ((a+b)+c)+d.

sans qu’il y ait moyen de spécifier que l’on veut une somme de quatre opérandes autrement que par l’appel, moins expressif, d’une fonction comme sum(a,b,c,d), Certes, par le moyen classique des expression-templates, on pourrait aboutir au résultat voulu, mais au prix de perdre le respect des parenthèses. Par exemple, dans le cas où le programmeur écrirait (a+b+c)+d, une expression-template pourrait grouper les trois premiers termes, mais finirait par y agréger le quatrième. A moins d’ajouter au standard C++ un opérateur d’appel externe (static operator ()), qui opérerait sur le résultat de a+b+c, il n’y a aucun moyen de garder trace des parenthèses.

Ceci est un problème sérieux pour une interface de programmation, car il est important de respecter l’intention exprimée par l’utilisateur.

Proposition pour le C++

Soit un type Expr sur lequel nous proposons la possibilité de définir l’opérateur + suivant:

enum ExprOperator { ..., OpSum, ... OpProduct, ...};
struct Expr { ExprOperator; std::vector<Expr> operands;};
std::vector<Expr> to_vector(Expr... ee);

Expr operator+(Expr e, Expr... ee) // <- proposition
{
    return Expr { OpSum, to_vector(e,ee...) };
}

A noter: ce programme compile, mais l’opérateur+ n’est compris qu’avec deux arguments, ce qui est un peu limité pour un deuxième argument variadique. Nous voulons qu’il soit accepté pour ce qu’il est. Avec ceci, nous sommes capables de traduire (a+b+c)+d en Expr{OpSum,{Expr{OpSum,{a,b,c}},d} respectant l’original.

Bien sûr, toutes les variantes prenant const Expr& et Expr&& sont possibles.

Conversions implicites ou opérations avec d’autres types

Bien souvent, ayant une variable a de type Expr, nous voulons lui ajouter un type numérique, par exemple 1, comme dans a+1 ou 1+a sans préciser a+Expr(1) ou Expr(1)+a, ce qui ruinerait l’élégance de notre expression. L’opérateur + binaire permet classiquement de traiter cela. Comment le généraliser à une somme à opérandes multiples, comme dans 1+a+b ou a+1+b?

Nous proposons d’utiliser les concepts en permettant d’écrire:

Expr operator+(Expr e, auto convertible_to<Expr>... ee)

Le problème est que nous n’avons pas toujours un type Expr en première position, comme dans le cas 1+a+b. Nous proposons donc que le C++ interprète cette définition comme: opérandes multiples dont un de type Expr et les autres de type convertible_to<Expr>.

Une façon peut-être plus acceptable pour les compilateurs serait d’écrire:

Expr operator+(auto convertible_to<Expr>... ee1, Expr e, auto convertible_to<Expr>... ee2)

dans lequel e serait la première parmi les opérandes qui soit de type Expr.

Cette possibilité serait réservée aux opérateurs associatifs non booléens (+,*,…). Pour les opérateurs booléens, une meilleure proposition est envisagée.