2016-09-30 3 views
10

У меня есть код, подобный этому:Как избежать такого рода повторения

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

мне интересно, есть простой способ, чтобы избежать повторения кода в ГЭТ (функции)?

+1

раздражающего право? Я соблазн создать макрос с константой или ничего, но это не очень похоже на C++. –

+1

У меня даже нет возможности повторно использовать функции без const_cast или сделать s изменяемым – Nick

+0

Ew @ right-aligned ampersands;) –

ответ

13

wandbox example

Альтернатива const_cast: создание функции static шаблона, который принимает *this в качестве ссылки:

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

Это работает из-за template argument deduction rules - в общем, TSelf будет принимать как const и не- const Рекомендации.

Если вам необходимо зарегистрироваться в this внутри get_def_impl, используйте self.member.

Кроме того, вы можете использовать std::conditional или аналогичных объектов внутри get_def_impl делать разные вещи в зависимости от const -ness от TSelf. Вы также можете использовать ссылку на пересылку (TSelf&&) и обрабатывать корпус, в котором перемещаются this благодаря ref-qualifiers и perfect-forwarding.

+2

Эй, хорошо .....! –

+0

Зачем вы когда-нибудь двигались '* this' (что, я полагаю, это то, что вы имели в виду)? –

+0

@LightnessRacesinOrbit: я имел в виду случаи, когда 'get_def' вызывается из временного экземпляра' A' ('auto get_def (/*...*/) && {/*...*/}'). Возможно, вам придется переместить некоторых членов 'A' внутри' get_def_impl' в этом случае –

0

Не отвечает на вопрос напрямую, но я обычно склоняюсь к const getter + non const setter - таким образом ваш класс получит уведомление, когда строка будет изменена и сможет действовать на нее (в будущем), если необходимо - без необходимости проходить и изменять все, что его использует.

+0

Не всегда подходит. Например, представьте, если 'std :: vector :: operator []' были 'const'-only, и вам пришлось передавать новые значения оптом через сеттер. –

+0

Согласен - не всегда; я действительно признаюсь, но у него есть свои преимущества в местах. – UKMonkey

1

В некоторых случаях использования можно также использовать шаблон функции, не являющиеся членов, как:

#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)); 
} 
Смежные вопросы