2 votes

Constructeur inutilisé instancié dans une classe modèle créée par un autre constructeur.

J'ai la classe suivante :

template <typename T=void>
class Foo{
public:

  Foo(){};

  template <typename = typename std::enable_if_t<!std::is_void<T>::value, std::nullptr_t>>
  Foo(const T&){};

};

int main() {
  Foo<void> v;
}

v est créé à l'aide du premier constructeur. Par conséquent, il n'est pas nécessaire de créer le second constructeur pour le module Foo<void> .

Pourquoi est-il créé de toute façon ?

Le problème est que la création explicite du second constructeur avec le type void contourne SFINAE, et essaie de faire un paramètre de const void& . Cela n'est évidemment pas autorisé.

Comment puis-je empêcher le deuxième constructeur d'être valide si T es void ?

2voto

max66 Points 4276

Pourquoi est-il créé de toute façon ?

Parce que dans le constructeur de votre modèle

template <typename = typename std::enable_if_t<!std::is_void<T>::value, std::nullptr_t>>
Foo(const T&){};

la valeur du test pour std::enable_if ( !std::is_void<T>::value ) dépend du type de modèle de la classe ( T ).

Pour activer/désactiver SFINAE une méthode d'une classe (ou d'une structure), vous devez écrire un test qui dépend d'un paramètre de la méthode elle-même.

Une façon de contourner ce problème est d'ajouter un paramètre de template U pour la méthode et lui donner T comme type par défaut. Je veux dire quelque chose comme

template <typename U = T,
          typename = std::enable_if_t<!std::is_void<U>::value, std::nullptr_t>>
Foo(const U&){} // ..... the test depends from U ---^
//        ^--- U also here, to avoid the void reference problem

ou, peut-être mieux,

template <typename U = T, 
          std::enable_if_t<!std::is_void<U>::value, std::nullptr_t> = nullptr>
Foo(const U&){}

0voto

Artyer Points 3473

Une alternative à la désactivation du constructeur est de remplacer const T& avec un type inutilisable lorsque T est nulle :

struct unusable {
  unusable() = delete;
  unusable(const unusable&) = delete;
  ~unusable() = delete;
};

template <typename T=void>
class Foo{
  using cref = std::conditional_t<std::is_void_v<T>, unusable, std::add_lvalue_reference_t<const T>>;
public:

  Foo(){}

  Foo(cref){}

};

C'est utile const T& il est utilisé à plusieurs endroits, de sorte que vous ne devez pas utiliser SFINAE partout.

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