我一直在考虑为我的C++项目编写一个C++,我偶然发现了以下代码:
#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调用标识,但没有传递任何标识。然而,identity返回转发的参数(在我的例子中,是一个std:: string ),并弹出字符串的最后面的字符。如果标识本身没有传递给转发的参数,那么标识如何返回转发的参数?
发布于 2016-06-13 00:37:10
f不调用identity -- f是用identity实例调用的。在这里遍历这两个案例:
static_if<std::is_same<std::string, T>::value>([&](auto f){
f(value).pop_back();
}).else_([&](auto f){
--f(value);
});如果T是std::string,那么我们实例化一个statement<true>,它的then()使用identity实例调用传入函数。第一个lambda的参数,f,将是identity类型的-因此f(value)实际上只是value,我们做value.pop_back()。
如果T不是std::string,那么我们实例化一个statement<false>,它的then()什么也不做,它的else_()用identity实例调用lambda。再说一遍,f(value)只是value,我们做的是--value。
这是static_if的一个非常令人困惑的实现,因为lambda中的f总是一个identity。这是必要的,因为我们不能直接使用value (不能编写value.pop_back(),因为那里没有依赖的名称,所以编译器会很高兴地确定它对于整数的格式不正确),所以我们只是将value的所有用法包装在一个依赖函数对象中,以延迟实例化(f(value)依赖于f,所以在提供f之前不能实例化--如果函数没有调用,就不会发生这种情况)。
更好的做法是实现它,以便实际将参数传递给lambda。
发布于 2016-06-13 00:56:37
template<typename F>
void then(const F& f){
f(identity());
}更具可读性,因为
template<typename F>
void then(const F& f){
f(identity{});
}它们正在构造标识对象,而不是调用标识对象。
这里的诀窍是,即使函数从未实例化,模板函数的非依赖部分也必须是有效的。
因此,当值是整数时,说value.pop_back()在lambda中是无效的。
通过将identity{}传递给then或else的情况之一,我们可以避免这个问题。
语句f(value)生成依赖类型。因此,只有当实际实例化了lambda的模板operator()时,它才需要有效(还必须有一些可能的f使其有效,但这是一个角的情况)。
由于我们只实例化条件告诉我们的路径,所以只要f(value)在所获取的分支中有效,它几乎可以以任何我们想要的方式使用。
我会称f为一个更好的名字,比如safe、guard、var或magic,而不是f。在简洁的代码中使用两个f的不同上下文增加了混乱。
发布于 2016-06-13 00:53:47
让我们来看看,您的Cond在static_if中是true,因此,将使用主模板类.
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是泛型lambda的一种类型(意思是,您可以使用任何类型调用f )。
template<typename F>
void then(const F& f){
f(identity());
}identity()创建一个类型为identity的对象,然后将其作为参数传递,以调用为您的泛型lambda。
[&](auto f){ //This is the lamda passed, it's a generic lambda
f(value).pop_back();
}但是回想一下,f是一个identity类型的对象,它有一个模板化的call ()操作符,它基本上返回传递给它的对象。
所以,我们就像这样:
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);
}
);
});https://stackoverflow.com/questions/37780389
复制相似问题