Cette question est ancienne, mais avec le C++11 nous avons une nouvelle façon de vérifier une des fonctions de l'existence (ou l'existence d'un non-membre de type, vraiment), en s'appuyant sur SFINAE de nouveau:
template<class T>
auto serialize_imp(std::ostream& os, T const& obj, int)
-> decltype(os << obj, void())
{
os << obj;
}
template<class T>
auto serialize_imp(std::ostream& os, T const& obj, long)
-> decltype(obj.stream(os), void())
{
obj.stream(os);
}
template<class T>
auto serialize(std::ostream& os, T const& obj)
-> decltype(serialize_imp(os, obj, 0), void())
{
serialize_imp(os, obj, 0);
}
Exemple vivant.
Maintenant, sur certaines explications. Première chose, j'utilise l'expression SFINAE à exclure l' serialize(_imp)
fonctions de résolution de surcharge, si la première expression à l'intérieur d' decltype
n'est pas valide (aka, la fonction n'existe pas).
L' void()
est utilisé pour faire le type de retour de l'ensemble de ces fonctions void
.
L' 0
argument est utilisé pour préférer l' os << obj
de surcharge si les deux sont disponibles (littérale 0
est de type int
et le premier de surcharge est un meilleur match).
Maintenant, vous voulez probablement un trait de vérifier si une fonction existe. Heureusement, il est facile d'écrire que. Notez, cependant, que vous devez écrire un trait, vous-même pour chaque nom de fonction, vous voudrez peut-être.
#include <type_traits>
template<class>
struct sfinae_true : std::true_type{};
namespace detail{
template<class T, class A0>
static auto test_stream(int)
-> sfinae_true<decltype(std::declval<T>().stream(std::declval<A0>()))>;
template<class, class A0>
static auto test_stream(long) -> std::false_type;
} // detail::
template<class T, class Arg>
struct has_stream : decltype(detail::test_stream<T, Arg>(0)){};
Exemple vivant.
Et sur les explications. Tout d'abord, sfinae_true
est un helper de type, et il revient au même que l'écriture d' decltype(void(std::declval<T>().stream(a0)), std::true_type{})
. L'avantage, c'est simplement que c'est plus court.
Ensuite, l' struct has_stream : decltype(...)
hérite de soit std::true_type
ou std::false_type
en fin de compte, selon que l' decltype
vérifier en test_stream
d'échec ou pas.
Dernier, std::declval
vous donne une "valeur" de ce type de col, sans que vous ayez besoin de savoir comment vous pouvez construire. Notez que ceci n'est possible à l'intérieur d'un non évaluée contexte, comme decltype
, sizeof
et les autres.
Notez que decltype
n'est pas forcément nécessaire, sizeof
(et non évaluée tous les contextes) a obtenu cette amélioration. C'est juste qu' decltype
offre déjà un type et en tant que telle est juste plus propre. Voici un sizeof
version de l'un des surcharges:
template<class T>
void serialize_imp(std::ostream& os, T const& obj, int,
int(*)[sizeof((os << obj),0)] = 0)
{
os << obj;
}
Exemple vivant.
L' int
et long
paramètres sont encore là pour la même raison. Le tableau de pointeur est utilisé pour fournir un contexte où l' sizeof
peut être utilisé.