2 votes

decltype ne déduit pas les membres const pour les objets const.

#include <type_traits>
#include <functional>

struct Chains
{};

struct Stages
{

    Chains mutating_chains;

    Chains sideffect_chains;

    Chains write_chains;

    void forall_chains(const std::function<void(Chains & chain)> & fun)
    {
        forall_chains(*this, fun);
    }

    void forall_chains(
        const std::function<void(const Chains & chain)> & fun) const
    {
        forall_chains(*this, fun);
    }

    template <typename Self>
    static void forall_chains(
        Self & self,
        const std::function<void(decltype(self.mutating_chains) & chain)> & fun)
    {
        fun(self.mutating_chains);
        fun(self.sideffect_chains);
        fun(self.write_chains);
    }
};

Il y a manifestement quelque chose que je ne comprends pas avec decltype . Parce que selon le message d'erreur que le compilateur jette, Self est déduit comme const Stages, alors pourquoi self.member n'est pas déduit comme const member ? Aussi, comment faire pour que cela fonctionne correctement, déduire les membres const pour les objets const ? J'ai ajouté des parenthèses à l'expression decltype((self.mutating_chains)) et qui a passé la compilation mais je ne suis pas sûr que ce soit la bonne chose à faire.

f.cpp: In instantiation of ‘static void Stages::forall_chains(Self&, const std::function<void(decltype (self.mutating_chains)&)>&) [with Self = const Stages; decltype (self.mutating_chains) = Chains]’:
f.cpp:150:33:   required from here
f.cpp:158:33: error: no match for call to ‘(const std::function<void(Chains&)>) (const Chains&)’
         fun(self.mutating_chains);

4voto

Vittorio Romeo Points 2559

J'ai ajouté des parenthèses à l'expression decltype((self.mutating_chains)) et qui a passé la compilation mais je ne suis pas sûr que ce soit la bonne chose à faire.

Oui, c'est la bonne chose à faire dans ce cas. En bref, decltype(x) vous donne le type déclaré de x qui ne dépend pas de la catégorie de valeur de l'expression.

Pour un expression lvalue x de type T , decltype((x)) au lieu de cela, on obtient T& ce qui, dans votre cas, donne le const a été appliqué correctement.

Vous pouvez trouver une explication plus formelle (et plus précise) sur le site de l page de référence cpp pour decltype(...) .


D'ailleurs, pensez à passer votre callback via une balise argument de modèle au lieu de std::function . Cette dernière n'est pas une abstraction à coût nul - c'est une enveloppe lourde qui utilise l'effacement de type dont l'utilisation doit être minimisée.

template <typename Self, typename F>
static void forall_chains(Self& self,  F&& fun){ /* ... */ }

J'ai écrit un article sur le sujet : passer des fonctions aux fonctions .

4voto

Quentin Points 3904

Oui, en effet. decltype a un cas particulier pour decltype(self.mutating_chains) (c'est moi qui souligne) :

Si l'argument est une expression d'id non parenthésée ou une expression d'accès à un membre de classe non parenthésé alors decltype donne le type de l'entité nommée par cette expression.

Ainsi, vous obtenez le type réel avec lequel self.mutating_chains a été déclaré, c'est-à-dire Chains .

Lorsque vous ajoutez des parenthèses, vous retombez dans le cas général, qui évalue en fait le type de l'expression, qui dans le cas d'un const Self es const Chains & comme prévu :

Si l'argument est une autre expression de type T et
a) [...]
b) si la catégorie de valeur de l'expression est lvalue, alors decltype donne T& ;
c) [...]

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