Utilisation des arguments de modèle ‘void’ en C ++

Prenons l’exemple minimal suivant:

using Type1 = std::function; template  using Type2 = std::function; Type1 whyDoesThisWork; Type2 andYetThisDoesNot; 

Si le deuxième alias de type, j’obtiens le message d’erreur “L’argument n’a peut-être pas le type ‘void'”. (J’ai testé avec Xcode 4.5, Clang / c ++ 11 / libc ++, OS X 10.7.)

Je trouve cela curieux: je me serais attendu à ce que les Type2 de Type1 et Type2 se comportent de manière identique. Que se passe t-il ici? Et existe-t-il un moyen de réécrire le deuxième alias de type afin que je puisse écrire Type2 et obtenir std::function au lieu d’une erreur?

Modifier Je devrais probablement append que la raison pour laquelle je veux ceci est de permettre quelque chose comme ce qui suit:

 template  using Continuation = std::function; auto someFunc = []() -> void { printf("I'm returning void!\n"); }; Continuation c; 

Continuation devient Continuation et j’obtiens l’erreur.

La réponse courte est “les modèles ne sont pas des substitutions de chaînes”. void f(void) n’a de sens que dans la mesure où il s’agit d’un alias pour void f() en C ++, afin d’être rétro-compatible avec C.

La première étape consiste à utiliser des variadics, comme indiqué ailleurs.

La deuxième étape consiste à déterminer comment mapper les fonctions de renvoi de void à … eh bien, peut-être quelque chose comme std::function , ou peut-être autre chose. Je dis peut-être autre chose parce que contrairement aux autres cas, vous ne pouvez pas appeler std::function foo; foo( []()->void {} ); std::function foo; foo( []()->void {} ); – ce n’est pas une vraie continuation.

Quelque chose comme ça peut-être:

 template struct Continuation { typedef std::function type; }; template<> struct Continuation { typedef std::function type; }; 

puis utilisez-le comme ceci:

 auto someFunc = []()->void {}; Continuation::type c; 

qui vous donne le type que vous voulez. Vous pouvez même append une application à la suite:

 template struct Continuation { typedef std::function type; template static void Apply( type const& cont, func&& f, Args... args) { cont( f(args...) ); } }; template<> struct Continuation { typedef std::function type; template static void Apply( type const& cont, func&& f, Args... args) { f(args...); cont(); } }; 

qui vous permet d’appliquer une continuation à une exécution d’une fonction de manière uniforme si le type entrant est un vide ou s’il s’agit d’un type non-vide.

Cependant, je demanderais “pourquoi voudriez-vous faire ceci”?

Je n’ai pas de réponse réelle, seulement ce que j’ai dit dans le commentaire: Vous ne pouvez pas avoir le type de fonction void , comme dans:

 int foo(int, char, void, bool, void, void); // nonsense! 

Je pense que T(void) n’est autorisé qu’en tant que notation de compatibilité pour C (qui distingue les déclarations et les prototypes , très différemment du C ++, et qui doit pouvoir dire “pas d’argument”).

Donc, la solution devrait être variadique:

 template  using myType = std::function; 

De cette façon, vous ne pouvez correctement avoir aucun argument :

 myType<> f = []() { std::cout << "Boo\n"; } 

Plusieurs réponses expliquent déjà le raisonnement. Pour append à ces réponses, la spécification dit (C ++ 11 §8.3.5 [dcl.func] / 4):

Une liste de parameters composée d’un seul paramètre non nommé de type non dépendant void équivaut à une liste de parameters vide. À l’exception de ce cas particulier, un paramètre ne doit pas avoir le type cv void .

Dans votre exemple de Type2 , le T in void(T) est un type dépendant – il dépend d’un paramètre de modèle.

Lorsqu’une fonction est déclarée comme prenant un paramètre de type void , comme dans std::function , c’est vraiment une façon loufoque de dire qu’elle ne prend aucun paramètre. Mais la façon dont vous avez déclaré Type2 est std::function avec une signature qui ne renvoie rien (void), mais qui prend 1 paramètre. void n’est pas un type pouvant être utilisé en tant que paramètre, c’est simplement une façon de déclarer qu’il n’y a pas de paramètre. Cela ne fonctionne donc pas avec Type2, car cela nécessite un type réel pouvant être utilisé en tant que paramètre.

Nul peut être interprété comme un paramètre vide si vous le transmettez à une fonction. Vous n’utilisez pas de pointeur vide après tout, donc

 void func (void) 

devient

 void func ()