如何解决这是lambda法律模板语法吗? 通用Lambda:C ++ 14功能具有显式模板参数列表的通用lambda:C ++ 20功能
在重构一些遗留代码时,我遇到了STL算法中使用的谓词的传统实现:
template<bool b>
struct StructPred {
bool operator()(S const & s) { return s.b == b; }
};
我很累并且撞到了鲍尔默峰,所以我不小心将它改写成这样的lambda,这看起来很自然,也很有效:
template<bool b>
auto lambda_pred = [] (S const & s) { return s.b == b; };
后来,我意识到我从未见过像这样的模板lambda。我在cppreference或stackoverflow上找不到类似的东西。生成模板lambda的规范方法似乎是将它们包装在模板结构或模板函数中。 C ++ 20为lambda引入了命名模板参数,但这是一种不同的语法(在捕获括号之后)。
现在我的问题是:这是合法语法吗?它记录在任何地方吗?它甚至是lambda还是其他东西?与包装器替代品相比,有什么影响或副作用吗?为什么每个人都推荐这种情况下的包装器实现?我缺少明显的东西吗?
下面和godbolt处的完整测试代码。只是为了确保我也添加了类型模板参数版本。 MSVC,GCC和clang对此代码感到满意。
#include <vector>
#include <algorithm>
struct S {
bool b = false;
};
// classic function object
template<bool b>
struct StructPred {
bool operator()(S const & s) { return s.b == b; }
};
// template function producing a lambda
template<bool b>
auto make_pred() {
return [] (S const & s) { return s.b == b; };
}
// direct template lambda
template<bool b>
auto lambda_pred = [] (S const & s) { return s.b == b; };
// also with type params
template<typename T,bool b>
auto lambda_pred_t = [] (T const & t) { return t.b == b; };
std::pair<size_t,size_t> count1(std::vector<S> const & v) {
return {
std::count_if(v.begin(),v.end(),StructPred<true>{}),std::count_if(v.begin(),StructPred<false>{})
};
}
std::pair<size_t,size_t> count2(std::vector<S> const & v) {
return {
std::count_if(v.begin(),make_pred<true>()),make_pred<false>())
};
}
std::pair<size_t,size_t> count3(std::vector<S> const & v) {
return {
std::count_if(v.begin(),lambda_pred<true>),lambda_pred<false>)
};
}
std::pair<size_t,size_t> count4(std::vector<S> const & v) {
return {
std::count_if(v.begin(),lambda_pred_t<S,true>),false>)
};
}
void test() {
std::vector<S> v{3};
v[1].b = true;
// all implementations correctly return {1,2}
auto c1 = count1(v);
auto c2 = count2(v);
auto c3 = count3(v);
auto c4 = count4(v);
}
解决方法
template<bool b>
auto lambda_pred = [] (S const & s) { return s.b == b; };
这实际上不是template-lambda,而是分配给lambda的variable template。
这不等同于向隐式声明的闭包struct
中添加模板参数,闭包使用此lambda作为调用运算符(传统方法):
template<bool b>
struct StructPred { // NOT equivalent to this
bool operator()(S const & s) { return s.b == b; }
};
struct StructPred { // NOT equivalent to this either
template<bool b>
bool operator()(S const & s) { return s.b == b; }
};
它等效于根据变量的模板参数创建不同的闭包。因此,对于bool
示例,这就像在以下一种类型的operator()
之间进行选择:
struct StructPred_true {
bool operator()(S const & s) { return s.b == true; }
}
struct StructPred_false {
bool operator()(S const & s) { return s.b == false; }
}
这种方法不允许部分专业化,因此功能较弱。
这种方法可能不受欢迎的另一个原因是,它不能让您轻松访问Closure类型。
与匿名类StructPred
和StructPred_true
StructPred_false
可以显式使用
C ++ 20中的lambda模板如下所示:
auto lambda = []<bool b>(S const & s){ return s.b == b; };
这等效于将闭包的operator()
模板化。
以下所有标准参考文献均引用N4659: March 2017 post-Kona working draft/C++17 DIS。
通用Lambda:C ++ 14功能
生成模板lambda的规范方法似乎是将它们包装在模板结构或模板函数中。 C ++ 20为lambda引入了命名模板参数,但这是不同的语法(在捕获括号之后)。
另一个答案彻底解释了OPs变量模板的构造,而这个答案解决了上面的强调部分;也就是说,通用Lambda是C ++ 14以来的语言功能,而不是仅C ++ 20起可用的功能。
根据[expr.prim.lambda.closure]/3 [摘录]:
[...]对于一般的lambda,闭包类型具有一个公共的内联函数调用操作员成员模板,该模板的template-parameter-list包含一个发明的类型template-parameter,用于每次
auto
在lambda的参数声明子句按出现顺序排列。 [...]
通用lambda可以声明为
auto glambda = [](auto a,auto b) { return a < b; };
与
相当struct anon_struct {
template<typename T,typename U>
bool operator()(T a,U b) { return a < b; }
}
不是
template<typename T,typename U>
struct anon_struct {
bool operator()(T a,U b) { return a < b; }
}
作为单个通用lambda对象(其 closure类型实际上不是类模板,而是非模板(非联合)类)必不可少的,可以用来泛型调用其功能调用操作员模板以获取其发明的模板参数的不同实例。
#include <iostream>
#include <ios>
int main() {
auto gl = [](auto a,auto b) { return a < b; };
std::cout << std::boolalpha
<< gl(1,2) << " " // true ("<int,int>")
<< gl(3.4,2.2) << " " // false ("<double,double>")
<< gl(98,'a'); // false ("<int,char>")
}
具有显式模板参数列表的通用lambda:C ++ 20功能
从C ++ 20开始,在声明通用lambda时,我们可以使用显式模板参数列表,并提供糖化语法,在调用时提供显式模板参数。 >通用Lambda。
在C ++ 14和C ++ 17中,对于通用lambda的模板参数只能隐式声明为lambda声明中每个已声明的auto
参数的发明的 type 模板参数,但具有以下限制:
- 发明的模板参数只能是合成的 type 模板参数(如上所示),并且
- 类型模板参数不能在lambda主体中直接访问,但是需要使用相应
decltype
参数上的auto
来提取。
或者,如一个人为的示例所示:
#include <type_traits>
// C++17 (C++14 if we remove constexpr
// and use of _v alias template).
auto constexpr cpp17_glambda =
// Template parameters cannot be declared
// explicitly,meaning only type template
// parameters can be used.
[](auto a,auto b)
// Inventend type template parameters cannot
// be accessed/used directly.
-> std::enable_if_t<
std::is_base_of_v<decltype(a),decltype(b)>> {};
struct Base {};
struct Derived : public Base {};
struct NonDerived {};
struct ConvertsToDerived { operator Derived() { return {}; } };
int main() {
cpp17_glambda(Base{},Derived{}); // Ok.
//cpp17_glambda(Base{},NonDerived{}); // Error.
// Error: second invented type template parameter
// inferred to 'ConvertsToDerived'.
//cpp17_glambda(Base{},ConvertsToDerived{});
// OK: explicitly specify the types of the invented
// type template parameters.
cpp17_glambda.operator()<Base,Derived>(
Base{},ConvertsToDerived{});
}
现在,在C ++ 20中,通过引入用于lambda的名称模板参数(以及require子句),可以将以上示例简化为:
#include <type_traits>
auto constexpr cpp20_glambda =
[]<typename T,typename U>(T,U)
requires std::is_base_of_v<T,U> { };
struct Base {};
struct Derived : public Base {};
struct NonDerived {};
struct ConvertsToDerived { operator Derived() { return {}; } };
int main() {
cpp20_glambda(Base{},Derived{}); // Ok.
//cpp20_glambda(Base{},NonDerived{}); // Error.
// Error: second type template parameter
// inferred to 'ConvertsToDerived'.
//cpp20_glambda(Base{},ConvertsToDerived{});
// OK: explicitly specify the types of the
// type template parameters.
cpp20_glambda.operator()<Base,ConvertsToDerived{});
}
我们还可以用模板参数声明lambda,这些参数不一定是模板参数:
#include <iostream>
#include <ios>
template<typename T>
struct is_bool_trait {
static constexpr bool value = false;
};
template<>
struct is_bool_trait<bool> {
static constexpr bool value = true;
};
template<typename T>
struct always_true_trait {
static constexpr bool value = true;
};
int main() {
auto lambda = []<
template<typename> class TT = is_bool_trait>(auto a) -> bool {
if constexpr (!TT<decltype(a)>::value) {
return true; // default for non-bool.
}
return a;
};
std::cout << std::boolalpha
<< lambda(false) << " " // false
<< lambda(true) << " " // true
<< lambda(0) << " " // true
<< lambda(1) << " " // true
<< lambda.operator()<always_true_trait>(0) << " " // false
<< lambda.operator()<always_true_trait>(1); // true
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。