2016-04-12 4 views
3

De nombreux assistants SFINAE de Boost sont apparus dans la bibliothèque std avec C++ 11, mais il semble que le has_dereference ne l'ait pas. En dehors de cette fonctionnalité, j'ai réussi à éliminer une dépendance Boost de mon paquet, et je voudrais m'en débarrasser complètement, alors comment faire pour obtenir le même effet en utilisant seulement les fonctionnalités std C++ 11?C++ 11 équivalent std de Boost has_dereference

+2

Ne peut pas vous regarder juste à la mise en œuvre? –

+0

Je ne connais pas exactement la licence, mais vous pouvez probablement copier l'implémentation de Boost dans votre code.Devrait être en-tête seulement et pas trop long. –

+0

@Nicol Je suppose que vous n'avez pas? ;-) Cela se fait via des #defines cachées (et complexes) comme BOOST_TT_FORBIDDEN_IF, qui sont utilisées par un en-tête has_prefix_operator.hpp interne, puis de nouveau indéfinies. Il n'est donc pas facile de faire de l'ingénierie inverse et certainement pas simplement de copier le code de Boost dans du code std. – andybuckley

Répondre

6

La manière la plus simple de vérifier si une classe a une fonction sans dépendance externe est généralement l'idiome void_t.

// Define this once in your project somewhere accessible 
template <class ... T> 
using void_t = void; 

L'astuce est toujours la même; vous définissez un modèle de classe qui hérite de std::false_type:

template <class T, class = void> 
struct has_dereference : std::false_type {}; 

Ceci est le modèle de classe « fallback ». Maintenant, nous allons définir une spécialisation qui ne fonctionne que lorsque le type a la propriété que nous voulons:

template <class T> 
struct has_dereference<T, void_t<decltype(*std::declval<T>())>> : std::true_type {}; 

Pour utiliser, il suffit de faire:

bool x = has_dereference<int*>::value; 
bool y = has_dereference<int>::value; 

etc.

J'ajouterai techniquement, operator* est en réalité une famille de fonctions; l'opérateur peut être qualifié à la fois CV et qualifié par catégorie de valeur. Chaque fois que vous effectuez une détection sur un type, vous effectuez une détection dans cette famille. Je ne vais pas aller plus dans les détails car il est rarement rencontré en pratique (operator* est rarement qualifié de catégorie de valeur, et l'opérateur a presque toujours une version const, et volatil monte rarement) mais il vaut la peine d'en être conscient au cas où vous verriez quelque chose de surprenant .

Cette technique mérite d'être connue, surtout si vous faites de la méta-programmation sans dépendances comme Boost ou Hana. Vous pouvez en savoir plus sur void_t ici: How does `void_t` work.

+1

Fonctionne bien. Je vous remercie! – andybuckley

4

Voici une petite aide à l'écriture de caractères SFINAE. Il utilise std::void_t, que vous pouvez réimplémenter si vous n'en avez pas.

namespace details { 
    template<template<class...>class Z, class v, class...Ts> 
    struct can_apply:std::false_type{}; 
    template<template<class...>class Z, class...Ts> 
    struct can_apply<Z, std::void_t<Z<Ts...>>, Ts...>:std::true_type{}; 
} 
template<template<class...>class Z, class...Ts> 
using can_apply=typename details::can_apply<Z, void, Ts...>::type; 

Une fois que vous avez cela, votre problème est facile.

template<class T> 
using deref_result = decltype(*std::declval<T>()); 

template<class T> 
using can_deref = can_apply<deref_result, T>; 

L'idée est ici pour cacher la machinerie std::void_t. Vous écrivez un trait qui exprime le "résultat d'un calcul", et à partir de cela nous pouvons obtenir "ce calcul est-il valide".

Un très void_t portable ressemble:

namespace details { 
    template<class...>struct voider{using type=void;}; 
} 
template<class...Ts> 
using void_t=typename voider<Ts...>::type; 

faire en une ligne quelques pauses compilateurs plus anciens, et la version 2 de la ligne est assez facile, pourrait tout aussi bien.

0

Une version modifiée de de Yakk:

template <class...> struct pack {}; 

namespace detail { 
    template<template <class...> class Z, class Pack, class = void> 
    struct can_apply_impl : std::false_type {}; 

    template<template<class...>class Z, class...Ts> 
    struct can_apply_impl<Z, pack<Ts...>, std::void_t<Z<Ts...>> > : std::true_type {}; 
} 
template<template<class...>class Z, class...Ts> 
using can_apply = detail::can_apply_impl<Z, pack<Ts...>>; 

DEMO

+0

ah, j'ai eu une faute de frappe. Fixe je pense! – Yakk

+0

@Yakk Confirmé. –

+0

Ce n'est pas vraiment une nouvelle réponse, c'est plutôt un commentaire sur Yakk qu'il a eu une faute de frappe? – Barry