Je sais que le code suivant ne se compilera pas.
void baz(int i) { }
void baz() { }
class Bar
{
std::function<void()> bazFn;
public:
Bar(std::function<void()> fun = baz) : bazFn(fun){}
};
int main(int argc, char **argv)
{
Bar b;
return 0;
}
Parce std::function
qu'on dit de ne pas considérer la résolution de surcharge, comme je l'ai lu dans cet autre post .
Je ne comprends pas bien les limites techniques qui ont forcé ce type de solution.
J'ai lu sur les phases de traduction et les modèles sur cppreference, mais je ne peux penser à aucun raisonnement auquel je n'ai pas trouvé de contre-exemple. Expliqué à un demi-profane (encore nouveau en C ++), quoi et pendant quelle étape de la traduction les éléments ci-dessus ne parviennent-ils pas à être compilés?
c++
templates
translation
std-function
TuRtoise
la source
la source
Réponses:
Cela n'a rien à voir avec les "phases de traduction". Il s'agit uniquement des constructeurs de
std::function
.Voir,
std::function<R(Args)>
ne nécessite pas que la fonction donnée soit exactement du typeR(Args)
. En particulier, il ne nécessite pas de disposer d'un pointeur de fonction. Il peut prendre n'importe quel type appelable (pointeur de fonction membre, un objet qui a une surcharge deoperator()
) tant qu'il est invocable comme s'il prenait desArgs
paramètres et renvoie quelque chose de convertible enR
(ou s'il l'R
estvoid
, il peut retourner n'importe quoi).Pour ce faire, le constructeur approprié de
std::function
doit être un modèle :template<typename F> function(F f);
. Autrement dit, il peut prendre n'importe quel type de fonction (sous réserve des restrictions ci-dessus).L'expression
baz
représente un ensemble de surcharge. Si vous utilisez cette expression pour appeler l'ensemble de surcharge, c'est très bien. Si vous utilisez cette expression comme paramètre d'une fonction qui prend un pointeur de fonction spécifique, C ++ peut réduire la surcharge définie en un seul appel, ce qui le rend très bien.Cependant, une fois qu'une fonction est un modèle et que vous utilisez la déduction d'arguments de modèle pour déterminer ce qu'est ce paramètre, C ++ n'a plus la possibilité de déterminer la surcharge correcte dans l'ensemble de surcharge. Vous devez donc le spécifier directement.
la source
function
modèle de classe n'est pas pertinent . Ce qui importe, c'est le paramètre de modèle sur le constructeur que vous appelez. Ce qui est justetypename F
: aka, n'importe quel type.La résolution de surcharge se produit uniquement lorsque (a) vous appelez le nom d'une fonction / opérateur, ou (b) vous le convertissez en un pointeur (vers une fonction ou une fonction membre) avec une signature explicite.
Ni l'un ni l'autre ne se produit ici.
std::function
prend tout objet compatible avec sa signature. Il ne prend pas spécifiquement un pointeur de fonction. (une lambda n'est pas une fonction std, et une fonction std n'est pas une lambda)Maintenant, dans mes variantes de fonction homebrew, pour la signature,
R(Args...)
j'accepte également unR(*)(Args...)
argument (une correspondance exacte) pour exactement cette raison. Mais cela signifie qu'il élève les signatures de "correspondance exacte" au-dessus des signatures "compatibles".Le problème principal est qu'un ensemble de surcharge n'est pas un objet C ++. Vous pouvez nommer un jeu de surcharge, mais vous ne pouvez pas le faire passer "nativement".
Maintenant, vous pouvez créer un ensemble de pseudo-surcharge d'une fonction comme celle-ci:
cela crée un seul objet C ++ qui peut effectuer une résolution de surcharge sur un nom de fonction.
En développant les macros, nous obtenons:
ce qui est ennuyeux à écrire. Une version plus simple, mais un peu moins utile, est ici:
nous avons un lambda qui prend n'importe quel nombre d'arguments, puis parfait les transmet à
baz
.Alors:
travaux. Nous reportons la résolution de surcharge dans le lambda dans lequel nous stockons
fun
, au lieu de passerfun
directement un ensemble de surcharge (qu'il ne peut pas résoudre).Il y a eu au moins une proposition pour définir une opération dans le langage C ++ qui convertit un nom de fonction en un objet d'ensemble de surcharge. Jusqu'à ce qu'une telle proposition standard soit dans la norme, la
OVERLOADS_OF
macro est utile.Vous pourriez aller plus loin et prendre en charge le pointeur de fonction de conversion en compatibilité.
mais cela commence à devenir obtus.
Exemple en direct .
la source
Le problème ici est qu'il n'y a rien qui indique au compilateur comment effectuer la fonction de décroissance du pointeur. Si tu as
Ensuite, le code fonctionnerait puisque maintenant le compilateur sait à quelle fonction vous voulez car il y a un type concret auquel vous assignez.
Lorsque vous utilisez,
std::function
vous appelez son constructeur d'objet fonction qui a la formeet puisqu'il s'agit d'un modèle, il doit déduire le type de l'objet transmis. comme il
baz
s'agit d'une fonction surchargée, aucun type unique ne peut être déduit, la déduction de modèle échoue et vous obtenez une erreur. Vous devez utiliserpour forcer un seul type et permettre la déduction.
la source
Au moment où le compilateur décide quelle surcharge passer dans le
std::function
constructeur, tout ce qu'il sait, c'est que lestd::function
constructeur est conçu pour prendre n'importe quel type. Il n'a pas la possibilité d'essayer les deux surcharges et de constater que le premier ne compile pas mais le second le fait.La façon de résoudre ce problème est d'indiquer explicitement au compilateur la surcharge que vous souhaitez avec un
static_cast
:la source