6 votes

Détecter l'idiome avec une fonction qui échoue à static_assert

Existe-t-il un moyen d'utiliser le idiome de détection (ou une autre méthode) pour tester si une fonction est valide pour des arguments de modèle donnés, s'il échoue à cause d'une erreur d'interprétation. static_assert ?

L'exemple ci-dessous illustre que la validité de foo (échec du calcul du type de retour) est détecté comme prévu, mais celui de bar (à défaut static_assert ) ne l'est pas.

#include <iostream>
#include <type_traits>

template <typename... T> using void_t = void;

template <class AlwaysVoid, template<class...> class Op, class... Args>
struct detector: std::false_type { };

template <template<class...> class Op, class... Args>
struct detector<void_t<Op<Args...>>, Op, Args...>: std::true_type { };

template <template<class...> class Op, class... Args>
constexpr bool is_detected = detector<void, Op, Args...>::value;

template <typename T>
std::enable_if_t<!std::is_void<T>::value> foo() {
  std::cout << "foo" << std::endl;
}

template <typename T>
void bar() {
  static_assert( !std::is_void<T>::value );
  std::cout << "bar" << std::endl;
}

template <typename T> using foo_t = decltype(foo<T>());
template <typename T> using bar_t = decltype(bar<T>());

int main(int argc, char* argv[]) {

  foo<int>();
  // foo<void>(); // fails as expected

  bar<int>();
  // bar<void>(); // fails as expected

  std::cout << std::boolalpha;

  // detection works for foo
  std::cout << is_detected<foo_t,int > << std::endl; // true
  std::cout << is_detected<foo_t,void> << std::endl; // false

  // but not for bar
  std::cout << is_detected<bar_t,int > << std::endl; // true
  std::cout << is_detected<bar_t,void> << std::endl; // true !!!
}

C'est la raison pour laquelle je ne peux pas détecter si une boost::lexical_cast est valable pour les types donnés.

6voto

AndyG Points 3298

Il n'est pas possible d'utiliser SFINAE pour obtenir la bonne sortie ici, car Règles du SFINAE agir sur déclarations et non des définitions.

Le type de bar tel que déclaré sera toujours void(void) La déclaration est donc correcte en ce qui concerne le SFINAE.

Si vous écrivez un véritable idiome de détection (Comme Je l'ai fait ici. ), et utilisez-le comme suit :

template <typename T> 
using CanCallFoo_t = decltype(&foo<T>);

template<class T>
using CanCallFoo = detect<T, CanCallFoo_t, void>;

template<class T>
using CanCallBar_t = decltype(&bar<T>);

template< class T>
using
CanCallBar = detect<T, CanCallBar_t, void>;

//...
std::cout << CanCallFoo<int>::value << std::endl; // true
std::cout << CanCallFoo<void>::value << std::endl; // false

std::cout << CanCallBar<int>::value << std::endl;
std::cout << CanCallBar<void>::value << std::endl;

Vous remarquerez que SFINAE réussit et qu'ensuite vous obtenez une erreur de compilation lorsque la définition est analysée.

erreur : échec de l'assertion statique
static_assert( !std::is_void<T>::value );

Démo

Remarquez qu'il fonctionne avec foo parce que foo Le type déclaré de l'utilisateur échouera à SFINAE pour void

Le point de static_assert est de faire échouer la compilation si aucune autre meilleure correspondance n'est trouvée, et non comme une substitution pour SFINAE.

1voto

Nir Friedman Points 3165

La réponse d'Andy est correcte, en expliquant que la détection d'un static_assert ou de tout échec de substitution de définition n'est pas possible avec sfinae. Je voulais cependant signaler qu'il est possible de résoudre votre problème, même si vous devrez supporter le poids de quelques répétitions.

En gros, vous devez trouver quels types d'opérations lexical_cast essaie de s'appliquer à un type générique. Ensuite, vous enveloppez lexical_cast avec votre propre fonction, et sfinae votre fonction sur n'importe quelles propriétés lexical_cast exige. Ce n'est pas élégant mais je doute qu'il y ait plus que quelques exigences sur le type pertinent pour votre application d'intérêt, donc c'est une solution pratique (peut-être).

C'est peut-être évident, mais j'ai pensé que je devais le mentionner puisque ce n'est pas encore couvert.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X