2016-09-30 3 views
10

J'ai un code similaire à ceci:Comment éviter ce genre de répétition

#include <string> 

class A{ 
public: 
    std::string &get(){ 
     return s; 
    } 

    const std::string &get() const{ 
     return s; 
    } 

    std::string &get_def(std::string &def){ 
     return ! s.empty() ? s : def; 
    } 

    // I know this might return temporary 
    const std::string &get_def(const std::string &def) const{ 
     return ! s.empty() ? s : def; 
    } 

private: 
    std::string s = "Hello"; 
}; 

Je me demande est-il moyen facile d'éviter la répétition de code dans les fonctions get()?

+1

droit ennuyeux? Je suis tenté de créer une macro avec const ou nothing, mais ce n'est pas très C++. –

+1

Je n'ai même pas un moyen de réutiliser les fonctions sans const_cast ou rendre s à mutable – Nick

+0

Ew @ esperluettes alignées à droite;) –

Répondre

13

wandbox example

Alternative à const_cast: la création d'une fonction de modèle static qui prend *this comme référence:

class A 
{ 
private: 
    template <typename TSelf, typename TStr> 
    static auto& get_def_impl(TSelf& self, TStr& def) 
    { 
     return !self.s.empty() ? self.s : def; 
    } 

public: 
    auto& get_def(std::string& str) 
    { 
     return get_def_impl(*this, str); 
    } 

    const auto& get_def(const std::string& str) const 
    { 
     return get_def_impl(*this, str); 
    } 
}; 

Cela fonctionne en raison de template argument deduction rules - bref, TSelf acceptera les deux const et non const les références.

Si vous devez accéder aux membres de this à l'intérieur de get_def_impl, utilisez self.member.

De plus, vous pouvez utiliser std::conditional ou des installations similaires à l'intérieur get_def_impl pour faire des choses différentes en fonction de la const ness de TSelf. Vous pouvez également utiliser une référence de renvoi (TSelf&&) et gérer le cas où this est déplacé grâce à ref-qualifiers et perfect-forwarding.

+2

Salut, gentil .....! –

+0

Pourquoi déménageriez-vous jamais (* ce que je présume être ce que vous vouliez dire)? –

+0

@ LightnessRacesinOrbit: ce que je voulais dire, c'était de gérer des cas où 'get_def' est appelé depuis une instance temporaire de' A' ('auto get_def (/*...*/) && {/*...*/}'). Vous devrez peut-être déplacer certains membres de 'A' dans' get_def_impl' dans ce cas –

0

Ne répond pas directement à la question, mais je préférerais généralement un accesseur const + non const set - de cette façon, votre classe sera notifiée quand la chaîne sera changée et pourra agir dessus (dans le futur) si nécessaire - sans avoir à passer par et changer tout ce qui l'utilise.

+0

Pas toujours approprié. Par exemple, imaginez si 'std :: vector :: operator []' était 'const' et que vous deviez passer de nouvelles valeurs en gros via un setter. –

+0

Je suis d'accord - pas toujours; c'est vraiment une façon de penser java/C# que j'admets, mais il y a des avantages dans les endroits. – UKMonkey

1

Dans certains cas d'utilisation, vous pouvez également utiliser la fonction modèle de non-membre comme:

#include <type_traits> 
#include <string> 

template <class U, class R = std::conditional_t<std::is_const<U>::value, std::string const&, std::string& >> 
R get(U &u) { 
    return u.s; 
} 

template <class U, class R = std::conditional_t<std::is_const<U>::value, std::string const&, std::string& >> 
R get_def(U &u, typename std::remove_reference<R>::type& def) { 
    return u.s.empty() ? u.s : def; 
} 

struct S { 
    template <class U, class R> 
    friend R get(U &); 
    template <class U, class R> 
    friend R get_def(U &, typename std::remove_reference<R>::type&); 
private: 
    std::string s; 
}; 

int main() { 
    S s; 
    get(s) = "abc"; 
    //get(static_cast<const S &>(s)) = "abc"; // error: passing ‘const std::basic_string<char>’ as ‘this’... 
    std::string s2 = get(static_cast<const S&>(s)); 
}