3
votes

Comment rendre le bloc static_assert réutilisable dans les classes de modèle?

Disons que j'ai une classe de modèle qui crée plusieurs static_asserts:

template <class T>
class Foo
{
    static_assert(!std::is_const<T>::value,"");
    static_assert(!std::is_reference<T>::value,"");
    static_assert(!std::is_pointer<T>::value,"");

    //...<snip>...
}

Maintenant, disons que j'ai plus de classes de modèle qui doivent faire les mêmes affirmations.

Existe-t-il un moyen de rendre un bloc static_assert réutilisable? Une "fonction static_assert" si vous voulez.


1 commentaires

Si j'ai bien compris ce que vous entendez par bloc static_assert , il semblerait que vous puissiez mettre ces assertions dans une classe de modèle et en hériter Foo . Faites-moi savoir si vous souhaitez du code.


4 Réponses :


4
votes

Vous pouvez simplement combiner les traits obligatoires en un seul avec le nom descriptif:

static_assert(std::is_fancy<T>::value,"");

et l'utiliser plus tard:

template<typename T> using
is_fancy = ::std::integral_constant
<
    bool
,   (not std::is_const<T>::value)
    and
    (not std::is_reference<T>::value)
    and
    (not std::is_pointer<T>::value)
>;


6 commentaires

Totalement une question de préférence, mais je préfère hériter de bool_constant .


@SergeyA Bon point, même si je déclarerais simplement un alias parce que l'héritage semble mettre plus de charge sur le compilateur.


Observation intéressante sur les coûts d'héritage au moment de la compilation par rapport aux alias. Jamais vérifié, comment avez-vous mesuré cela?


@SergeyA instancier un nouveau type est généralement plus difficile que de résoudre un alias


@GuillaumeRacicot bien sûr, mais je me demande si l'effet est quantifiable.


@SergeyA "mesure" serait probablement un terme audacieux, mais depuis un certain temps, je me bats avec les échecs de compilation causés par le compilateur (gcc) à court de RAM sur une base de code relativement lourde en modèles. Et limiter l'apparition de nouveaux types de modèles (en particulier de manière récursive) s'est avéré être le moyen le plus efficace de les contrer.



3
votes

Une chose que vous pouvez faire est de créer un nouveau trait qui est une conjonction des traits que vous souhaitez vérifier. Puisque vous voulez la négation de tous ces traits qui se traduiraient littéralement par

template<typename T>
using my_trait = std::disjunction<std::is_const<T>,
                                  std::is_reference<T>,
                                  std::is_pointer<T>>;

static_assert(!my_trait<int>::value, "");

mais devoir utiliser std :: negation pour chaque trait est / peut être une douleur . Vous pouvez vous en débarrasser en utilisant std :: disjunction pour obtenir un "ou" de tous les traits, puis annuler simplement la valeur dans l'assertion statique comme vous le faites, ce qui vous donne

template<typename T>
using my_trait = std::conjunction<std::negation<std::is_const<T>>,
                                  std::negation<std::is_reference<T>>,
                                  std::negation<std::is_pointer<T>>>;

static_assert(my_trait<int>::value, "");


2 commentaires

@JeJo C'est une autre option valable. Il existe plusieurs façons d'écorcher ce chat. Je ne voulais simplement pas introduire de variables réelles.


@SergeyA Wow. J'ai raté ça. Merci pour le spot. J'ai mis à jour la réponse.



3
votes

Vous pouvez définir un constexpr bool qui effectue l'évaluation au moment de la compilation:

template<typename Type, typename Enable = void> class Class1;

template<typename Type>
class Class1<Type, std::enable_if_t<is_okay_type<Type>> >
{
    //...code...
};

Puis soit:

  1. utilisez-le directement static_assert , comme vous l'avez fait dans votre exemple:

    template<typename T> class MyClass
    {
        static_assert(is_okay_type<T>, "message");
    public:
        //...code...
    };
    
  2. ou vous pouvez effectuer une instanciation conditionnelle de la classe de modèle , selon l'argument du modèle.

    template<typename T>
    inline constexpr bool is_okay_type = !std::is_const<T>::value &&
                                         !std::is_reference<T>::value &&
                                         !std::is_pointer<T>::value;
    


0 commentaires

3
votes

J'ai vu quelques bonnes réponses, en utilisant la conjonction. Malheureusement, ceux-ci sont vraiment difficiles à déboguer. Une fois, j'ai eu à déboguer un problème avec ma classe en déclarant: exigences satisfaites. C'était une liste trop longue à comprendre. J'ai finalement copié tous les chèques sous-jacents un par un.

Dans la mesure du possible, j'aime les séparer:

 constexpr static CustomCheck<T> check{};

Dans votre classe, vous n'avez qu'à l'instancier pour obtenir la vérification et une erreur détaillée:

template<typename T>
struct CustomCheck {
     static_assert(check<T>);
      // ...
 };


0 commentaires