web-dev-qa-db-fra.com

Pourquoi le compilateur ne génère-t-il pas d'erreurs de compilation si un type d'argument incorrect est passé à une liste d'initialisation de struct?

J'ai défini une structure qui a un constructeur:

struct MyStruct
{
    MyStruct(const int value)
        : value(value)
    {
    }
    int value;
};

et les objets suivants:

int main()
{
    MyStruct a (true);
    MyStruct b {true};
}

Mais je n'ai reçu aucune erreur de compilation, ni avec MVS2015 ni avec Xcode 7.3.1.

  1. Pourquoi est-ce que je ne reçois aucune erreur de compilation?
  2. Comment puis-je faire en sorte que le compilateur m'aide à détecter cela? (Initialement, la structure était écrite pour contenir des données bool, mais après un certain temps, le code a été modifié et bool est devenu int et plusieurs bogues ont été introduits.)
39
T M

Une bool peut être implicitement convertie en une int de manière à préserver la valeur. Les seules conversions interdites avec initialisation d'accolade sont rétrécissement conversions (par exemple, l'inverse de bool{42}).

Si vous voulez vous assurer que votre classe est constructible only avec int, la méthode directe consiste simplement à delete à tous les autres constructeurs:

struct MyStruct
{
    explicit MyStruct(int i) : value(i) { }

    template <typename T>
    MyStruct(T t) = delete;

    int value;
};

Ici, MyStruct{true} et MyStruct(false) donneront des appels à MyStruct::MyStruct<bool>, défini comme supprimé et donc mal formé. 

L'avantage de ceci par rapport à static_assert est que tous les caractères de type donneront les bonnes valeurs. Par exemple, std::is_constructible<MyStruct, bool> est std::false_type

71
Barry

Voici une construction qui vous permet d'initialiser votre classe à partir d'une valeur int:

#include <type_traits>

struct MyStruct
{
    template <typename T>
    MyStruct(T t) : value(t)
    {
        static_assert(std::is_same<T, int>::value, "Bad!");
    }

    int value;
};

En effet, la déduction d’argument de modèle requise par ce modèle de constructeur produira le type exact de l’argument et ne réalisera pas de conversions. Vous pourrez donc effectuer des tests sur ce type.

Vous devriez peut-être aussi ou plutôt utiliser SFINAE pour contraindre le constructeur, de sorte que MyStruct ne se présente pas comme constructible à partir de rien.

En outre, vous devriez probablement également créer le modèle de constructeur explicit afin que les entiers aléatoires ne deviennent pas des instances MyStruct.

En d'autres termes, je l'écrirais comme suit:

struct MyStruct
{
    template <typename T,
              typename = std::enable_if_t<std::is_same<T, int>::value>>
    MyStruct(T t) : value(t) {}

    // ...
15
Kerrek SB

La solution la plus simple est de déclarer un constructeur bool comme supprimé, n'est-ce pas?

struct MyStruct
{
    MyStruct(bool) = delete;

    MyStruct(const int value)
    : value(value)
    {
    }
    int value;
};

exemple de sortie d'erreur:

...
/Users/xxxxxxx/play/fast_return/skeleton/main.cpp:68:14: error: call to deleted constructor of 'MyStruct'
    MyStruct b {true};
             ^ ~~~~~~
/Users/xxxxxxx/play/fast_return/skeleton/main.cpp:57:9: note: 'MyStruct' has been explicitly marked deleted here
        MyStruct(bool) = delete;
        ^
2 errors generated.
8
Richard Hodges

Parce que bool peut être implicitement converti en int.

Ceci est une fonctionnalité de langue que vous ne pouvez pas désactiver, désolé.

la valeur true est convertie en valeur 1 (int).

0
Thomas Papamihos