Pourquoi remove_reference ne fonctionne-t-il pas sur les fonctions?

38

Ran dans quelque chose d'étrange lors de la métaprogrammation de modèle l'autre jour. Cela revient essentiellement à cette affirmation qui ne passe pas (comme je m'y attendais).

static_assert(std::is_same_v<void(), std::remove_reference_t<void()&>>);

Au début, je pensais que je faisais une erreur syntaxique en définissant une référence de fonction, mais cette assertion passe, montrant que ce n'est pas le cas.

static_assert(std::is_same_v<void()&, void()&>);

J'ai également essayé remove_referencede m'implémenter en copiant la source à partir de cppreference mais cela n'a pas fonctionné non plus. Qu'est-ce qui se passe ici?

Artikash dit de réintégrer Monica
la source

Réponses:

42

Bienvenue dans le monde des types de fonctions abominables.

void() &n'est pas une référence à void(). La façon de l'orthographier serait void(&)()(ce qui si vous remove_reference_t, vous en reviendriez void()- c'est-à-dire que remove_reference_t cela fonctionne sur les références aux fonctions, si ce que vous fournissez est en fait une référence au type de fonction).

Ce à quoi void() &se réfère réellement est le type d'une fonction membre qualifiée de référence après avoir supprimé la classe. C'est:

struct C {
    void f() &;
};

Le type de &C::fest void (C::*)() &. Mais tous les pointeurs vers les membres peuvent être écrits comme T C::*pour certains types T, et dans ce cas, le type le Tserait void() &.

Voir aussi P0172 .

Barry
la source
3
Quelqu'un devrait créer une question canonique pour les types de fonctions abominables.
Brian
Wow, C ++ ne manque jamais de me surprendre même si je l'ai appris et utilisé pendant près de 10 ans.
Kelvin Hu
13

Le type que vous avez n'est pas une référence à une fonction, mais une fonction avec un qualificatif de référence .

static_assert(std::is_same_v<void()&, void()&>);
static_assert(!std::is_same_v<void()&, void(&)()>);
static_assert(std::is_same_v<void(&)(), void(&)()>);
static_assert(std::is_same_v<void(), std::remove_reference_t<void(&)()>>);
0x5453
la source