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

C++ 模板 SFINAE 问题

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

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

    模板定义:

    #include <utility>
    
    template <typename F, typename... Args, typename = decltype(std::declval<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 不好意思,我没有把条件说清澈,是我的锅
    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   ·   实用小工具   ·   2859 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 14:47 · PVG 22:47 · LAX 06:47 · JFK 09:47
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.