10

I have code similar to this:

#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";
};

I am wondering is there easy way to avoid code repetition in get() functions?

Nick
  • 8,479
  • 2
  • 36
  • 64

3 Answers3

13

wandbox example

Alternative to const_cast: creating a static template function that takes *this as a reference:

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);
    }
};

This works because of template argument deduction rules - in short, TSelf will accept both const and non-const references.

If you need to access members of this inside get_def_impl, use self.member.

Additionally, you can use std::conditional or similar facilities inside get_def_impl to do different things depending on the const-ness of TSelf. You can also use a forwarding reference (TSelf&&) and handle the case where this is being moved thanks to ref-qualifiers and perfect-forwarding.

Vittorio Romeo
  • 82,972
  • 25
  • 221
  • 369
  • Why would you ever move `*this` (which I presume is what you meant)? – Lightness Races in Orbit Sep 30 '16 at 09:21
  • @LightnessRacesinOrbit: what I meant was handling cases where `get_def` is being called from a temporary `A` instance (`auto get_def(/*...*/) && { /*...*/ }`). You may need to move some members of `A` inside `get_def_impl` in that case – Vittorio Romeo Sep 30 '16 at 09:23
  • Could you give an example? – Lightness Races in Orbit Sep 30 '16 at 09:34
  • @LightnessRacesinOrbit: [this is very contrived](http://melpon.org/wandbox/permlink/gSjoHxOerggYsePV) and there's probably a much better way of doing this, but let's assume that you have two `static` repetition helpers and two sets of ref-qualified accessors. If I call `A{}.get()` and I want **both** `&&`-qualified versions of the accessors to be called without any code repetition or unnecessary moves I need to deal with the *rvalueness* of `*this` in the `static` helpers. If you can simplify this to not use forwarding-references for `*this`, that would be great. – Vittorio Romeo Sep 30 '16 at 10:06
  • `*this` is always an _lvalue_. Doesn't matter that it represents a temporary object. But that's precisely why you need the `std::move` so okay :) – Lightness Races in Orbit Sep 30 '16 at 10:10
1

In some use cases you could also make use of non-member function template like:

#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));
}
W.F.
  • 13,347
  • 2
  • 29
  • 70
0

Doesn't answer the question directly, but I'd usually lean towards a const getter + non const setter - that way your class will get notified when the string is changed and can act on it (in the future) if needed - without having to go through and change everything that uses it.

UKMonkey
  • 6,473
  • 3
  • 17
  • 29