2017-07-15 3 views
5

J'utilise la structure is_callable définie comme suitVérifiez si une fonction est appelable

template <typename F, typename... Args> 
struct is_callable { 
    template <typename U> 
    static auto test(U* p) -> decltype((*p)(std::declval<Args>()...), void(), std::true_type()); 

    template <typename U> 
    static auto test(...) -> decltype(std::false_type()); 

    static constexpr bool value = decltype(test<F>(nullptr))::value; 
}; 

J'utilise ceci pour tester un lambda déclaré comme:

template <typename T> 
struct runner { 
    T t; 

    template <typename F, typename = typename std::enable_if<is_callable<F, T&>::value || is_callable<F, T&&>::value>::type> 
    void run(F&& f) { 
    return f(t); 
    } 
}; 

runner<int> a{0}; 
a.run([&] (auto& x) { 
    x++; 
}); 

Pourquoi cette compilation échouent sur la enable_if sur AppleClang? Est-ce que les auto ne devraient pas être déduites correctement?

+0

[Fonctionne pour moi] (http://coliru.stacked-crooked.com/a/b83c21fddc4e15cb). Quelle est votre version 'clang' et quel est le message d'erreur spécifique? – Rakete1111

+0

@ Rakete1111 'modèle candidat ignoré: désactivé par 'enable_if'' Apple LLVM version 8.1.0 (clang-802.0.42) – subzero

+0

Pourquoi vérifiez-vous s'il est callable avec' T & '* ou *' T && '? Vous l'appelez avec «T &', vérifiez juste cela. – Barry

Répondre

1

Votre cas pour le true_typetest semble faux. En tout cas, votre code est plus compliqué qu'il ne devrait l'être. Essayez l'exemple de travail minimale suivante:

#include <utility> 

template<typename F, typename...Args> struct is_callable { 
    template<typename F2, typename...Args2> static constexpr std::true_type 
    test(decltype(std::declval<F2>()(std::declval<Args2>()...)) *) { return {}; } 

    template<typename F2, typename...Args2> static constexpr std::false_type 
    test(...) { return {}; } 

    static constexpr bool value = decltype(test<F, Args...>(nullptr))::value; 
}; 

void f0(); 
static_assert(is_callable<decltype(f0)>::value, "f0()"); 
static_assert(!is_callable<decltype(f0), int>::value, "f0(0)"); 

int f1(int); 
static_assert(!is_callable<decltype(f1)>::value, "f1()"); 
static_assert(is_callable<decltype(f1), int>::value, "f1(0)"); 
static_assert(!is_callable<decltype(f1), int, int>::value, "f1(0, 0)"); 

auto __attribute__((unused)) f2 = [](int, char *) { return 7; }; 
static_assert(is_callable<decltype(f2), int, char *>::value, "f2(int, char *)"); 
static_assert(!is_callable<decltype(f2), int, int>::value, "f2(int, int)"); 
+1

C'est un * horrible * 'is_callable'. –

+0

Qu'est-ce qui est horrible? Il fonctionne avec C++ 11, 14 et 17, et utilise SFINAE très explicitement et clairement. – user3188445

0

Le problème n'est pas la classe de test is_callable, mais votre utilisation de celui-ci.

Lorsque vous utilisez std::enable_if dans la liste des arguments de modèle d'une fonction , vous devez l'utiliser de cette façon:

template <typename T> 
struct runner { 
    T t; 

    template 
    < 
    typename F, 
    std::enable_if_t<is_callable<std::decay_t<F>, T&>::value || is_callable<F, T&&>::value>* = nullptr 
    > 
    void run(F&& f) { 
    return f(t); 
    } 
}; 

Le formulaire que vous avez essayé d'utiliser est destiné à être utilisé comme un type de retour de fuite:

template<typename F> 
    auto run(F&& f) 
    -> std::enable_if_t<is_callable<std::decay_t<F>, T&>::value || is_callable<F, T&&>::value> 
    { 
    return f(t); 
    } 

Le type is_callable est correct. Il ressemble à celui que j'ai posté pour empiler le débordement avant.

+1

Pourquoi pensez-vous que vous * devez * l'utiliser de cette façon? – Barry

+0

@Barry car c'est l'une des 3 bonnes façons d'activer une fonction: type * = nullptr, type de fin ou faux argument. –

+0

Je ne sais pas pourquoi vous pensez que c'est le cas. 'template <..., typename = std :: enable_if_t <...>>' est assez communément utilisé. – Barry