我需要实现一个检测类型,比如 is_invocable
,但看起来 SFINAE 只对替换失败进行了浅层检查,而使用我的 is_invocable
我需要能够优雅地检测该调用是否会编译。是否可以使用 C++17 实现?
https://godbolt.org/z/Y72Dov
#include <type_traits>
struct supported_by_f1_and_f2 {};
struct not_supported_by_f1 {};
struct not_supported_by_f2 {};
template<typename T>
auto f2(T t, std::enable_if_t<!std::is_same_v<T, not_supported_by_f2>>* = 0) {}
template<typename T>
auto f1(T t, std::enable_if_t<!std::is_same_v<T, not_supported_by_f1>>* = 0) {
return f2(t);
}
template <typename T, typename = void>
struct is_f1_invocable : public std::false_type {};
template <typename T>
struct is_f1_invocable<T, std::void_t<decltype(f1(std::declval<T>()))>> : public std::true_type {};
using supported_by_f1_and_f2_ok_t = is_f1_invocable<supported_by_f1_and_f2>;
using not_supported_by_f1_ok_t = is_f1_invocable<not_supported_by_f1>;
using not_supported_by_f2_ok_t = is_f1_invocable<not_supported_by_f2>;
supported_by_f1_and_f2_ok_t supported_by_f1_and_f2_ok;
not_supported_by_f1_ok_t not_supported_by_f1_ok;
// Why substitution failure, that occures during 'return f2(t);', is not detected here during the instantiation of 'is_f1_invocable'?
not_supported_by_f2_ok_t not_supported_by_f2_ok; // error: no matching function for call to 'f2'
编辑:
来自 https://en.cppreference.com/w/cpp/language/sfinae :
那么有没有办法扩展/解决这个问题?
最佳答案
您正在寻找的概念是让 f1
对 SFINAE 友好。这需要 f1
的作者采取一些措施来确保用户有某种方法可以检测到对 f1
的调用是否格式错误,从而导致软错误。如果 f1
没有编写为 SFINAE 友好的,则没有解决方法。
为了使 f1
SFINAE 友好,我们需要确保在实例化 f1
的主体时会发生一些编译错误之前,首先,导致该错误的条件使 f1
的签名无效,以便当封闭的实例化尝试调用或获取 f1
的地址,SFINAE 开始从重载集中删除 f1
,因为在实例化 f1
签名的直接上下文中遇到错误。
换句话说,在这种情况下,由于我们认为 f2(t)
主体中调用 f1
的实例化可能会导致错误,我们应该在 f1
的签名中复制该调用。例如,我们可以这样做:
template <typename T>
auto f1(T t, std::enable_if_t<...>* = 0) -> decltype(f2(t)) { // actually you may want to decay the type but w/e
return f2(t);
}
所以现在,
f1(std::declval<T>())
的实例化启动了 f1
的替换和推导过程,它启动了 f2
的替换和推导过程。在这一点上,由于 enable_if
,在 f2
的签名中发生了替换失败,该签名位于 f2
实例化的直接上下文中,因此从重载集中删除了 f2
模板。因此,f2
签名中对 f1
的调用必须从一个空的重载集解析,这意味着重载解析失败是在 f1
实例化的直接上下文中。最后,这也从重载集中删除了 f1
模板,再次导致由于空重载集而导致重载解析失败,这次是在 is_f1_invocable
实例化的直接上下文中,这正是我们想要的。类似地,如果实例化
f2
的主体时出现问题,那么我们需要修改 f2
的签名以考虑这种可能性,并确保以类似方式传播 SFINAE。当然,你必须决定你想走多远。在某些时候,您可能会决定此时您确实想要导致硬错误,而不是简单地从重载集中删除签名,将软错误传播到封闭的实例化中。
关于c++ - 如何执行 "deep"SFINAE,即当替换在代码中进一步导致一些编译错误时?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/56842207/