我一直在为我的C ++项目编写一个static_if,我偶然发现了以下代码:
#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
中的重载operator()。它采用类型为T的rhs,称为x,cool和all。但是当调用identity
时,实际上没有任何内容传递给身份。
template<typename F>
void then(const F& f){
f(identity());
}
上面,f呼叫身份,但不传递身份。然而,identity返回转发的参数(在我的例子中,是一个std :: string),并弹出字符串的最后面的字符。 当身份没有传递给它的参数转发时,身份如何返回转发的参数?
答案 0 :(得分: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);
});
如果T
是std::string
,那么我们实例化一个statement<true>
,其then()
使用identity
实例调用传入函数。第一个lambda f
的参数将是identity
类型 - 所以f(value)
实际上只是value
而我们value.pop_back()
。
如果T
不是std::string
,那么我们实例化statement<false>
then()
什么也不做,else_()
调用lambda,实例为{ {1}}。同样identity
只是f(value)
而我们value
。
这是--value
的一个令人困惑的实现,因为lambda中的static_if
始终是f
。这样做是必要的,因为我们无法直接使用identity
(无法写value
,因为那里没有依赖名称,所以编译器会愉快地确定因为整数格式不正确,所以我们只是将value.pop_back()
的所有用法包含在依赖函数对象中以延迟实例化(value
依赖于{{1因此,在提供f(value)
之前不能实例化 - 如果函数未被调用则不会发生这种情况。
最好实现它,以便实际将参数传递给lambda。
答案 1 :(得分:1)
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
的不同上下文会增加混淆。
答案 2 :(得分:0)
让我们来看一下Cond
中true
为static_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
是通用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
类型的对象,并且有一个模板化调用()
运算符,它基本上返回传递给它的对象。
所以,我们这样做:
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);
}
);
});