这是lambda法律模板语法吗? 通用Lambda:C ++ 14功能具有显式模板参数列表的通用lambda:C ++ 20功能

如何解决这是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类型。 与匿名类StructPredStructPred_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 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-