web-dev-qa-db-fra.com

Empêche la fonction de prendre const std :: string & d'accepter 0

Vaut mille mots:

#include<string>
#include<iostream>

class SayWhat {
    public:
    SayWhat& operator[](const std::string& s) {
        std::cout<<"here\n"; // To make sure we fail on function entry
        std::cout<<s<<"\n";
        return *this;
    }
};

int main() {
    SayWhat ohNo;
    // ohNo[1]; // Does not compile. Logic prevails.
    ohNo[0]; // you didn't! this compiles.
    return 0;
}

Le compilateur ne se plaint pas en passant le numéro 0 à l'opérateur de parenthèse acceptant une chaîne. Au lieu de cela, cela compile et échoue avant d'entrer dans la méthode avec:

terminate called after throwing an instance of 'std::logic_error'
  what():  basic_string::_S_construct null not valid

Pour référence:

> g++ -std=c++17 -O3 -Wall -Werror -pedantic test.cpp -o test && ./test
> g++ --version
gcc version 7.3.1 20180303 (Red Hat 7.3.1-5) (GCC)

Ma conjecture

Le compilateur utilise implicitement le constructeur std::string(0) pour entrer la méthode, ce qui génère le même problème (google l'erreur ci-dessus) sans raison valable.

Question

Y a-t-il un moyen de résoudre ce problème côté classe, afin que l'utilisateur de l'API ne le ressente pas et que l'erreur soit détectée au moment de la compilation?

Autrement dit, ajouter une surcharge

void operator[](size_t t) {
    throw std::runtime_error("don't");
}

n'est pas une bonne solution.

97
kabanus

La raison pour laquelle std::string(0) est valide est due au fait que 0 Est une constante de pointeur nulle. Donc 0 correspond au constructeur de chaîne prenant un pointeur. Ensuite, le code va à l'encontre de la condition préalable selon laquelle on ne peut pas passer un pointeur nul à std::string.

Seul le littéral 0 Serait interprété comme une constante de pointeur nul, s'il s'agissait d'une valeur d'exécution dans un int vous n'auriez pas ce problème (car alors la résolution de surcharge chercherait un int conversion à la place). Le littéral 1 N'est pas non plus un problème, car 1 N'est pas une constante de pointeur nul.

Puisqu'il s'agit d'un problème de temps de compilation (valeurs littérales invalides), vous pouvez l'attraper au moment de la compilation. Ajoutez une surcharge de ce formulaire:

void operator[](std::nullptr_t) = delete;

std::nullptr_t Est le type de nullptr. Et cela correspondra à any constante du pointeur nul, que ce soit 0, 0ULL Ou nullptr. Et puisque la fonction est supprimée, elle provoquera une erreur de temps de compilation pendant la résolution de surcharge.

Une option consiste à déclarer une surcharge private de operator[]() qui accepte un argument intégral et ne le définit pas.

Cette option fonctionnera avec toutes les normes C++ (à partir de 1998), contrairement aux options comme void operator[](std::nullptr_t) = delete qui sont valides à partir de C++ 11.

Faire de la operator[]() un membre private provoquera une erreur diagnostiquable sur votre exemple ohNo[0], sauf si cette expression est utilisée par une fonction membre ou friend de la classe.

Si cette expression est utilisée à partir d'une fonction membre ou friend de la classe, le code sera compilé mais - puisque la fonction n'est pas définie - généralement la construction échouera (par exemple une erreur de l'éditeur de liens due à une fonction non définie).

26
Peter