2016-06-13 56 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。简洁代码中使用两个不同的上下文会增加混淆。