2016-09-30 97 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"; 
}; 

我想知道有沒有簡單的方法來避免重複的代碼中的get()函數?

+1

討厭吧?我很想用const或nothing創建一個宏,但它不是非常類似C++的。 –

+1

我甚至沒有辦法在沒有const_cast的情況下重用這些函數,或者使其成爲可變的 – Nick

+0

Ew @右對齊的&符號;) –

回答

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引用。

如果您需要訪問get_def_impl內部的this的成員,請使用self.member

此外,還可以使用std::conditional或類似設施內get_def_impl取決於const -ness的TSelf做不同的事情。您還可以使用轉發參考TSelf&&並處理this正在移動的情況,這要歸功於ref-qualifiersperfect-forwarding

+2

嘿,不錯.....! –

+0

爲什麼你會移動'* this'(我認爲這是你的意思)? –

+0

@LightnessRacesinOrbit:我的意思是處理從臨時的'A'實例('auto get_def(/*...*/)&& {/*...*/}')調用'get_def'的情況。您可能需要在'get_def_impl'內移動'A'的某些成員,在這種情況下 –

0

不直接回答這個問題,但我通常會傾向於一個const getter +非const setter - 這樣,當字符串發生變化並且可以對其執行操作時(將來),您的類將得到通知if需要 - 無需通過並更改所有使用它的內容。

+0

並不總是合適的。例如,想象一下,如果'std :: vector :: operator []'僅僅是'const',你必須通過setter來批量傳遞新值。 –

+0

我同意 - 不總是;這真是一種java/c#的思維方式,我會承認的,但它在地方有它的優勢。 – 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)); 
}