web-dev-qa-db-fra.com

Surcharge de l'opérateur [] et PAS de l'erreur "lvalue requise comme opérande gauche de l'affectation"

C'est un peu l'inverse de toutes les questions d'erreur "lvalue requise comme opérande gauche d'affectation".
J'ai une classe qui surcharge l'opérateur [], mais seulement la version qui renvoie un temporaire. S'il devait retourner un int:

struct Foo
{
    int operator[]( int idx ) const { return int( 0 ); }
};

Foo f;
f[1] = 5;

J'obtiendrais à juste titre l'erreur du compilateur lvalue. S'il retourne un type de structure cependant, le compilateur (GCC 7.2 dans ce cas) ne se plaint pas du tout:

struct Bar {};
struct Foo
{
    Bar operator[]( int idx ) const { return Bar(); }
};

Foo f;
f[1] = Bar();

Pourquoi ne se plaindrait-il pas de la même manière si Bar est un intérimaire et qu'il n'a pas d'opérateur spécialisé =? Autre question, existe-t-il un moyen de porter plainte? Il s'agit clairement d'une erreur de codage si elle devait être utilisée de cette façon

16
ByteMe95

y a-t-il un moyen de porter plainte?

Vous pouvez utiliser un opérateur d'affectation explicitement par défaut avec un qualificatif ref:

struct Bar {
    Bar& operator=(const Bar&) & = default;
//                             ^

Cela rend l'affectation d'une valeur r mal formée, tandis que l'affectation d'une valeur l reste bien formée.

Notez que la déclaration de l'opérateur d'affectation désactive l'affectation de déplacement implicite, vous devrez donc peut-être le définir également, si nécessaire (également par défaut, et éventuellement avec un qualificatif rvalue ref, le cas échéant).

Pourquoi ne se plaindrait-il pas de la même manière si Bar est un intérimaire et n'a pas d'opérateur spécialisé =?

Parce que les opérateurs d'affectation générés implicitement ne sont pas qualifiés de référence.

Il s'agit clairement d'une erreur de codage si elle devait être utilisée de cette façon

L'attribution d'une valeur r n'est pas universellement une erreur. Pour certains types qui sont censés se comporter comme des références, l'affectation d'une valeur r est naturelle. En effet, l'affectation modifie l'objet référencé et non l'objet temporaire lui-même.

Un cas d'utilisation typique consiste à affecter une valeur r _ std::tie (exemple de cppreference ):

std::set<S> set_of_s; // S is LessThanComparable

S value{42, "Test", 3.14};
std::set<S>::iterator iter;
bool inserted;

// unpacks the return value of insert into iter and inserted
std::tie(iter, inserted) = set_of_s.insert(value);

Oui, il pourrait être préférable que les opérateurs implicites soient qualifiés et qu'une déclaration explicite soit requise pour les non-qualifiés, étant donné que les types référentiels sont exceptionnels plutôt que la norme. Mais ce n'est pas ainsi que la langue est et la changer est un changement incompatible en arrière.

17
eerorika

Oui, il existe un moyen d'en faire une erreur de compilation en supprimant ces méthodes:

Bar& operator=(const Bar&)&& =delete;
Bar& operator=(Bar&&)&& =delete;

Notez simplement que cela désactivera la génération automatique des autres opérateurs et constructeurs, vous devez donc tous les définir:

struct Bar {
    Bar()=default;
    Bar(const Bar&) = default;
    Bar& operator=(const Bar&)&& =delete;
    Bar& operator=(Bar&&)&& =delete;
    Bar& operator=(const Bar&)& =default;
    Bar& operator=(Bar&&)& =default;
};
1
Quimby