Как избежать такого повторения
У меня есть код, похожий на этот:
#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";
};
Мне интересно, есть ли простой способ избежать повторения кода в функциях get()?
Ответы
Ответ 1
пример wandbox
Альтернатива 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);
}
};
Это работает из-за правил вывода аргументов шаблона - короче говоря, TSelf
будет принимать ссылки const
и non const
,
Если вам нужно получить доступ к элементам this
внутри get_def_impl
, используйте self.member
.
Кроме того, вы можете использовать std::conditional
или подобные объекты внутри get_def_impl
для выполнения разных действий в зависимости от const
-ness TSelf
. Вы также можете использовать ссылку пересылки (TSelf&&
) и обрабатывать случай, когда this
перемещается благодаря ref-qualifiers и совершенная переадресация.
Ответ 2
В некоторых случаях вы также можете использовать шаблон функции, не являющийся членом, например:
#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));
}
Ответ 3
Не отвечает на вопрос напрямую, но я обычно склоняюсь к const getter + non const setter - таким образом ваш класс получит уведомление, когда строка будет изменена, и может действовать на нее (в будущем), если это необходимо - без необходимости проходить и изменять все, что его использует.