C++ 模板 SFINAE 问题 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Tony042
V2EX    C++

C++ 模板 SFINAE 问题

  •  
  •   Tony042 2020-06-19 11:13:21 +08:00 2170 次点击
    这是一个创建于 2004 天前的主题,其中的信息可能已经有所发展或是发生改变。

    在读<<C++ Templates>>第二版时,有一个模板问题,代码如下:

    模板定义:

    #include <utility> template <typename F, typename... Args, typename = decltype(std::declvl<F>()(std::declval<Args &&>()...))> std::true_type isValidImpl(void *); template <typename F, typename... Args> std::false_type isValidImpl(...); inline constexpr auto isValid = [](auto f) { return [](auto &&... args) { return decltype(isValidImpl<decltype(f), decltype(args) &&...>(nullptr)){}; }; }; template <typename T> struct TypeT { using Type = T; }; template <typename T> constexpr auto type = TypeT<T>{}; template <typename T> T valueT(TypeT<T>); constexpr auto isDefaultCOnstructible= isValid([](auto x) -> decltype((void)decltype(valueT(x))()) {}); 

    如果一个 lambda 函数不执行是不是就不检查返回类型合法性了呢,比如在执行isDefaultConstructible(type<x>)命令时,会引入一个 lamda 函数,这个 lambda 函数返回值是decltype((void)decltype(valueT(x))()) {}), decltype(valueT(x))()在 x 是 int 的时候合法,导致最终调用第一个isValidImpl函数,x 是 int&的时候不合法,把第一个isValidImpl SFINAE out,调用第二个此优先级isValidImpl函数,即使 isValid 传入的是一个不合法 lambda 函数,但是由于没有使用所以没有检查 lambda 函数合法性,我的理解正确吗?

    第 1 条附言    2020-06-19 13:13:41 +08:00
    这个模板是可以跑通的,只不过用了 lambda 闭包,需要 C++17 及以上标准,已经在 GCC10 和 MSVC 16.9 都测试过了,没问题的,isDefaultConstructible 是一个 lamda 函数,用来返回类型是否有默认构造函数,如果有默认构造函数则返回 std::true_type 否则返回 std::false_type, 用法是 isConstructible(type<int>),当然你也可以再定义一个类型模板 template <typename T>
    using isDefaultCOnstructibleT= decltype(isDefaultConstructible(type<T>));
    15 条回复    2020-07-28 16:27:13 +08:00
    GeruzoniAnsasu
        1
    GeruzoniAnsasu  
       2020-06-19 12:50:23 +08:00
    这。。 写的啥东西

    强烈建议你自己丢到 clion 里去调一调这是在干什么

    首先莫名其妙的 inline 是哪来的?
    然后最后这个 isDefaultConstructible 是啥意思? 它是一个 is_valid 返回的 lambda,接受任意多个参数


    我觉得这坨面条可能跟你预期的完全不一样
    Tony042
        2
    Tony042  
    OP
       2020-06-19 13:11:24 +08:00
    @GeruzoniAnsasu 这个是可以跑通的,只不过用了 lambda 闭包,需要 C++17 及以上标准,已经在 GCC10 和 MSVC 16.9 都测试过了,没问题的,isDefaultConstructible 是一个 lamda 函数,用来返回类型是否有默认构造函数,如果有默认构造函数则返回 std::true_type 否则返回 std::false_type, 用法是 isConstructible(type<int>),当然你也可以再定义一个类型模板 template <typename T>
    using isDefaultCOnstructibleT= decltype(isDefaultConstructible(type<T>));
    Tony042
        3
    Tony042  
    OP
       2020-06-19 13:13:17 +08:00
    @GeruzoniAnsasu 然后这个是 C++ templates 2nd edition 19.4.3 的一个例子,我有个地方不太理解,就发上来一下,至于 inline 我推测作者放在这的位置是为了优化性能,不过 inline 这个还是要看编译器,应该没影响的,这个模板函数看起来确实比较唬人,不过仔细看应该还是可以理解的
    Tony042
        4
    Tony042  
    OP
       2020-06-19 13:14:20 +08:00
    @GeruzoniAnsasu 不好意思,我没有把条件说清澈,是我的锅
        5
    GeruzoniAnsasu  
       2020-06-19 13:48:49 +08:00
    行 8 那用法解决之后

    实际上逻辑就是检查 typename = decltype(std::declval<F>()(std::declval<Args &&>()...)) 合不合法,进一步来说,就只是检查 declval<传进去的那个参数&&>() 合不合法

    这不就跟 stl 的实现一样的
    hankai17
        6
    hankai17  
       2020-06-19 14:04:12 +08:00
    头晕 好复杂
    wutiantong
        7
    wutiantong  
       2020-06-22 10:34:17 +08:00
    --- 如果一个 lambda 函数不执行是不是就不检查返回类型合法性了呢?

    你的这个问题没提对,因为代码中相关处所涉及的并非普通的 lambda 而是 generic lambda,就你的这个问题这两者是完全不同的。
    Tony042
        8
    Tony042  
    OP
       2020-06-22 21:19:42 +08:00
    @wutiantong 不好意思,我这个提问题提的确实有不妥,那这个 generic lambda 是不是由于不实例化就不检查返回类型合法性了呢
    wutiantong
        9
    wutiantong  
       2020-06-23 13:25:03 +08:00
    @Tony042 是啊,generic lambda 的 operator() 是个模板成员函数,所以在实例化之前什么都不会发生。
    Wirbelwind
        10
    Wirbelwind  
       2020-06-23 15:06:28 +08:00
    不是因为你的 lambda 不合法,如果不合法,编译过不去。

    注意 std::false_type 那个函数,parameter 是...,匹配任何参数,所以才可以匹配到这个函数。

    当匹配不到 std::true_type 时候,其他情况都匹配 std::false_type,declval 是不求值才用的。

    当 lambda 也不匹配的时候,也都匹配 std::false_type
    Wirbelwind
        11
    Wirbelwind  
       2020-06-23 15:09:31 +08:00
    请问楼主看的具体(全名)是哪本书,求推荐
    Tony042
        12
    Tony042  
    OP
       2020-06-23 20:56:58 +08:00
    @Wirbelwind 返回值是有可能不合法的,decltype((void)decltype(valueT(x))()) ,当 x 是 type(int&)的时候就不合法,书名就叫<<C++ Templates>> 2nd edition
    Wirbelwind
        13
    Wirbelwind  
       2020-06-24 09:42:22 +08:00
    @Tony042 你说的不合法只是进了那个 std::false_type 的函数,这是你自己定义的
    Wirbelwind
        14
    Wirbelwind  
       2020-06-24 09:43:24 +08:00
    @Wirbelwind 推断使用了那个函数 表达式本身不求值
    BasIrs
        15
    BasIrs  
       2020-07-28 16:27:13 +08:00
    我们可以这样定义一个 Lambda 函数:
    #include <iostream>
    int main(){
    auto f1 = [] () { std::cout << "I am Lambda"; };//省略函数类型
    auto f2 = [] () -> int {
    std::cout << "I am Lambda too";
    return 1;
    };//函数类型后置
    }
    -----来自百度
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     770 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 28ms UTC 21:41 PVG 05:41 LAX 13:41 JFK 16:41
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86