J'écris une classe de modèle qui stocke un std::function afin de l'appeler plus tard. Voici le code simplifié:

template <typename T>
struct Test
{
    void call(T type)
    {
        function(type);
    }
    std::function<void(T)> function;
};

Le problème est que ce modèle ne se compile pas pour le type void car

void call(void type)

Devient indéfini.

Le spécialiser pour le type void ne résout pas le problème car

template <>
void Test<void>::call(void)
{
    function();
}

Est toujours incompatible avec la déclaration de call(T Type).

Donc, en utilisant les nouvelles fonctionnalités de C ++ 11, j'ai essayé std::enable_if:

typename std::enable_if_t<std::is_void_v<T>, void> call()
{
    function();
}

typename std::enable_if_t<!std::is_void_v<T>, void> call(T type)
{
    function(type);
}

Mais il ne compile pas avec Visual Studio:

erreur C2039: 'type': n'est pas membre de 'std :: enable_if'

Comment aborderiez-vous ce problème?

6
Mark Morrisson 28 nov. 2017 à 03:45

3 réponses

Meilleure réponse

SFINAE ne fonctionne pas sur (uniquement) les paramètres de modèle de la classe / structs.

Fonctionne sur les méthodes de modèle avec des conditions impliquant des paramètres de modèle de la méthode.

Il faut donc écrire

   template <typename U = T>
   std::enable_if_t<std::is_void<U>::value> call()
    { function(); }

   template <typename U = T>
   std::enable_if_t<!std::is_void<U>::value> call(T type)
    { function(type); } 

Ou, si vous voulez être sûr que U est T

   template <typename U = T>
   std::enable_if_t<   std::is_same<U, T>::value
                    && std::is_void<U>::value> call()
    { function(); }

   template <typename U = T>
   std::enable_if_t<std::is_same<U, T>::value
                    && !std::is_void<U>::value> call(T type)
    { function(type); } 

P.s .: std::enable_if_t est un type donc ne nécessite pas typename avant.

P.s.2: vous avez balisé C ++ 11 mais votre exemple utilise std::enable_if_t, c'est-à-dire C ++ 14, et std::is_void_v, c'est-à-dire C ++ 17

2
max66 28 nov. 2017 à 02:03

Si vous ne vous en tenez pas à utiliser void et que votre intention est de pouvoir utiliser Test sans aucun paramètre, alors utilisez un modèle variadique:

template <typename ...T>
struct Test
{
    void call(T ...type)
    {
        function(type...);
    }
    std::function<void(T...)> function;
};

De cette façon, vous pouvez avoir n'importe quel nombre de paramètres. Si vous ne voulez pas de paramètres, utilisez ceci:

Test<> t;
t.call();

Ce n'est donc pas exactement la syntaxe que vous vouliez, mais il n'y a pas besoin de spécialisation, et cette solution est plus flexible, car prend en charge n'importe quel nombre de paramètres.

2
geza 28 nov. 2017 à 01:52

Spécialisez toute la classe:

template <>
struct Test<void>
{
    void call()
    {
        function();
    }
    std::function<void()> function;
};
4
Jarod42 28 nov. 2017 à 01:12
47522213