C++ 模板 SFINAE 问题

2020-06-19 11:13:21 +08:00
 Tony042

在读<<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 函数合法性,我的理解正确吗?

1848 次点击
所在节点    C++
15 条回复
GeruzoniAnsasu
2020-06-19 12:50:23 +08:00
这。。 写的啥东西

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

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


我觉得这坨面条可能跟你预期的完全不一样
Tony042
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
2020-06-19 13:13:17 +08:00
@GeruzoniAnsasu 然后这个是 C++ templates 2nd edition 19.4.3 的一个例子,我有个地方不太理解,就发上来一下,至于 inline 我推测作者放在这的位置是为了优化性能,不过 inline 这个还是要看编译器,应该没影响的,这个模板函数看起来确实比较唬人,不过仔细看应该还是可以理解的
Tony042
2020-06-19 13:14:20 +08:00
@GeruzoniAnsasu 不好意思,我没有把条件说清澈,是我的锅
GeruzoniAnsasu
2020-06-19 13:48:49 +08:00
行 8 那用法解决之后

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

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

你的这个问题没提对,因为代码中相关处所涉及的并非普通的 lambda 而是 generic lambda,就你的这个问题这两者是完全不同的。
Tony042
2020-06-22 21:19:42 +08:00
@wutiantong 不好意思,我这个提问题提的确实有不妥,那这个 generic lambda 是不是由于不实例化就不检查返回类型合法性了呢
wutiantong
2020-06-23 13:25:03 +08:00
@Tony042 是啊,generic lambda 的 operator() 是个模板成员函数,所以在实例化之前什么都不会发生。
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
2020-06-23 15:09:31 +08:00
请问楼主看的具体(全名)是哪本书,求推荐
Tony042
2020-06-23 20:56:58 +08:00
@Wirbelwind 返回值是有可能不合法的,decltype((void)decltype(valueT(x))()) ,当 x 是 type(int&)的时候就不合法,书名就叫<<C++ Templates>> 2nd edition
Wirbelwind
2020-06-24 09:42:22 +08:00
@Tony042 你说的不合法只是进了那个 std::false_type 的函数,这是你自己定义的
Wirbelwind
2020-06-24 09:43:24 +08:00
@Wirbelwind 推断使用了那个函数 表达式本身不求值
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;
};//函数类型后置
}
-----来自百度

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/682968

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX