2016-06-13 40 views
3

我一直在尋找到寫static_if我的C++的項目,我偶然發現了下面的一段代碼:的std ::向前和運營商()

#include <iostream> 
using namespace std; 

namespace static_if_detail { 

struct identity { 
    template<typename T> 
    T operator()(T&& x) const { 
     return std::forward<T>(x); 
    } 
}; 

template<bool Cond> 
struct statement { 
    template<typename F> 
    void then(const F& f){ 
     f(identity()); 
    } 

    template<typename F> 
    void else_(const F&){} 
}; 

template<> 
struct statement<false> { 
    template<typename F> 
    void then(const F&){} 

    template<typename F> 
    void else_(const F& f){ 
     f(identity()); 
    } 
}; 

} //end of namespace static_if_detail 

template<bool Cond, typename F> 
static_if_detail::statement<Cond> static_if(F const& f){ 
    static_if_detail::statement<Cond> if_; 
    if_.then(f); 
    return if_; 
} 

template<typename T> 
void decrement_kindof(T& value){ 
    static_if<std::is_same<std::string, T>::value>([&](auto f){ 
     f(value).pop_back(); 
    }).else_([&](auto f){ 
     --f(value); 
    }); 
} 


int main() { 
    // your code goes here 
    std::string myString{"Hello world"}; 
    decrement_kindof(myString); 
    std::cout << myString << std::endl; 
    return 0; 
} 

這一切對我來說很有意義,除了有一件事:重載運算符()在struct identity。它採用稱爲x的T型rhs,很酷,全部。但是當調用identity時,實際上沒有任何東西傳入身份。

template<typename F> 
void then(const F& f){ 
    f(identity()); 
} 

上面,f調用標識,但沒有傳遞任何標識。然而,身份返回轉發的參數(在我的情況下,一個std ::字符串),並彈出字符串的最後面的字符。 身份如何返回一個轉發的參數,當它本身沒有參數傳遞給它轉發?

+1

對我來說,它看起來像'f'是一個函數,它是用'identity'類型的單個參數調用的。換句話說,'identity()'構造了傳遞給'f'函數的'identity'類型的一個實例。 –

+1

[This](http://ideone.com/p8SlI9)是它看起來像構建和調用一個類似的對象。 –

回答

4

f不叫identity - f被稱爲identity的實例。走在這裏的兩種情況:

static_if<std::is_same<std::string, T>::value>([&](auto f){ 
    f(value).pop_back(); 
}).else_([&](auto f){ 
    --f(value); 
}); 

如果Tstd::string,那麼我們實例化一個statement<true>then()調用與identity實例傳入的功能。第一個lambda的參數f將爲identity - 因此f(value)實際上只是value而我們做的是value.pop_back()

如果Tstd::string,那麼我們實例化一個statement<false>then()什麼都不做,而其else_()調用與identity實例的拉姆達。再次f(value)只是value,我們做--value


這是static_if一個非常混亂的實現,因爲f在lambda始終是一個identity。有必要這樣做,因爲我們不能直接使用value(不能寫value.pop_back(),因爲在那裏沒有依賴名,所以編譯器會高興地確定它對於整數是不合格的),所以我們只是將value的所有用法都包含在一個依賴函數對象來延遲實例化(f(value)依賴於f,所以不能實例化,直到提供f - 如果函數未被調用則不會發生這種情況)。

它會更好地實現它,以便您實際上將參數傳遞給lambda。

+1

或者至少將其稱爲'id'或'var'或'safe'而不是'f'。 – Yakk

+0

這個答案聽起來正確,+1,但它仍然使「身份」在設計層面上成爲一個謎。爲什麼不能例如通過數字'42'而不是'identity'的實例? –

+0

@ Cheersandhth.-Alf爲此添加了解釋。 – Barry

0

讓我們看看你的Condtruestatic_if的情況,因此主要的模板類將被使用...

template<bool Cond> 
struct statement { 
    template<typename F> 
    void then(const F& f){ 
     f(identity()); 
    } 

    template<typename F> 
    void else_(const F&){} 
}; 

回想一下,你的調用函數是:

static_if<std::is_same<std::string, T>::value> 
(
    [&](auto f){ //This is the lamda passed, it's a generic lambda 
     f(value).pop_back(); 
    } 
).else_(
    [&](auto f){ 
     --f(value); 
    } 
); 

在下面的應用功能,F是一類是通用拉姆達(意思是,你可以調用f與任何類型)

template<typename F> 
void then(const F& f){ 
    f(identity()); 
} 

identity()創建identity類型,然後將其作爲參數傳遞,以通話您的通用拉姆達的對象。

[&](auto f){ //This is the lamda passed, it's a generic lambda 
     f(value).pop_back(); 
    } 

但召回,fidentity類型的對象,並且具有模板呼叫()運算符,它基本上返回傳遞給它的對象。


所以,我們是這樣的:

void decrement_kindof(std::string& value){ 
    static_if<std::is_same<std::string, std::string>::value>([&](auto f){ 
     f(value).pop_back(); 
    }).else_([&](auto f){ 
     --f(value); 
    }); 
}); 

簡化爲:

void decrement_kindof(std::string& value){ 
    static_if<true>([&](auto f){ 
     f(value).pop_back(); 
    }).else_([&](auto f){ 
     --f(value); 
    }); 
}); 

簡化爲:

void decrement_kindof(std::string& value){ 
    static_if<true>(
     [&](identity ident){ 
      auto&& x = ident(value); //x is std::string() 
      x.pop_back(); 
     } (identity()) //<-- the lambda is called 

    ).else_(
     [&](auto f){ //This is not evaluated, because it's not called by the primary template of `statement` 
      --f(value); 
     } 
    ); 
}); 
1
template<typename F> 
void then(const F& f){ 
    f(identity()); 
} 

莫可讀爲

template<typename F> 
void then(const F& f){ 
    f(identity{}); 
} 

他們正在構建一個身份對象,而不是調用一個身份對象。

這裏的技巧是,即使函數從未實例化,模板函數的非依賴部分也必須是有效的。

因此,在值爲整數時,value.pop_back()從未在λ內有效。

通過將identity{}正確地轉換爲thenelse的情況下,我們可以避免這個問題。

陳述f(value)產生一個依賴類型。所以它只需要在lambda的模板operator()實際實例化時有效(還必須有一些可能的f使其有效,但這是一個特例)。

由於我們只是實例化條件告訴我們的路徑,因此只要在採取的分支中有效,f(value)幾乎可以用於我們想要的任何方式。

我會叫f一個更好的名字,像safeguardvarmagic而非f。簡潔代碼中使用兩個不同的上下文會增加混淆。