2017-05-05 1 views
2
#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 est évidemment quelque chose que je ne peux pas comprendre avec decltype. Parce que selon le message d'erreur que le compilateur lance, Self est déduit comme const Stages, alors pourquoi self.member n'est pas déduit comme membre const? Aussi comment le faire fonctionner correctement, déduire des membres const pour les objets const? J'ai ajouté des parenthèses à l'expression decltype((self.mutating_chains)) et cela a passé la compilation mais je ne suis pas sûr si c'est la bonne chose à faire.decltype ne déduit pas les membres const pour les objets const

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); 
+0

Vous pouvez mettre le code dans un f.cpp et le compiler et il va lancer ce message d'erreur exactement – mkmostafa

Répondre

4

J'ajouté entre parenthèses à l'expression decltype((self.mutating_chains)) et que la compilation passé, mais je ne sais pas si cela est la bonne chose à faire.

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

Pour un lieu expression lvaluex de type T, decltype((x)) cède T&, ce qui dans votre cas obtient le qualificatif const appliqué correctement. Vous pouvez trouver une explication plus formelle (et précise) sur le cppreference page for decltype(...).


Soit dit en passant, s'il vous plaît envisager l'adoption de votre rappel via un argument de modèle au lieu de std::function. Ce dernier n'est pas une abstraction à coût nul - c'est un emballage lourd qui utilise un effacement de type dont l'utilisation devrait être minimisée. J'ai écrit un article sur le sujet: passing functions to functions.

+1

merci pour la réponse et les conseils de bonus :) – mkmostafa

4

Oui, en effet. decltype a un cas particulier pour decltype(self.mutating_chains) (Souligné par):

Si l'argument est un sans parenthèses id-expression ou une expression d'accès membre de classe sans parenthèses, puis 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ée, c'est Chains.

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

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