首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >SFINAE不适用于递归函数

SFINAE不适用于递归函数
EN

Stack Overflow用户
提问于 2022-05-09 13:53:42
回答 2查看 371关注 0票数 7

让我们创建竞逐函数。

代码语言:javascript
复制
template <typename TFunc, typename TArg>
class CurryT
{
public:
    CurryT(const TFunc &func, const TArg &arg)
      : func(func), arg(arg )
        {}

    template <typename... TArgs>
        decltype(auto) operator()(TArgs ...args) const
            { return func(arg, args...); }

private:
    TFunc func;
    TArg  arg ;
};

template <typename TFunc, typename TArg>
    CurryT<decay_t<TFunc>, remove_cv_t<TArg>>
        Curry(const TFunc &func, const TArg &arg)
            { return {func, arg}; }

以及将函数解耦为单个参数函数的函数:

代码语言:javascript
复制
// If single argument function (F(int)).
template <typename F>
    static auto Decouple(const F &f, enable_if_t<is_invocable_v<F, int>> * = nullptr)
    {
        return f;
    }

// If multiple arguments function (F(int, int, ...)).
template <typename F>
    static auto Decouple(const F &f, enable_if_t<!is_invocable_v<F, int>> * = nullptr)
    {
        return [f](int v) { return Decouple( Curry(f, v) ); };
    }

如果传递了2个参数函数,一切都可以正常工作:

代码语言:javascript
复制
auto f1 = Decouple(
    [](int a, int b)
        { std::cout << a << " " << b << std::endl; }
);
f1(3)(4); // Outputs 3 4

但是如果我再加上更多的论点

代码语言:javascript
复制
auto f2 = Decouple(
    [](int a, int b, int c)
        { std::cout << a << " " << b << " " << c << std::endl; }
);
f(5)(6)(7);

编译中断:https://coliru.stacked-crooked.com/a/10c6dba670d17ffa

代码语言:javascript
复制
main.cpp: In instantiation of 'decltype(auto) CurryT<TFunc, TArg>::operator()(TArgs ...) const [with TArgs = {int}; TFunc = main()::<lambda(int, int, int)>; TArg = int]':

main.cpp:17:26: error: no match for call to '(const main()::<lambda(int, int, int)>) (const int&, int&)'
   17 |             { return func(arg, args...); }

它中断了std::is_invocable的实例化。

由于调试标准库很难,所以我创建了标准类型特性类的简单版本:

代码语言:javascript
复制
template <typename F> true_type  check(const F &, decltype( declval<F>()(1) )* );
template <typename F> false_type check(const F &, ...);

template <typename F>
    struct invocable_with_int : decltype(check(declval<F>(), nullptr))
        {};

template <typename F>
    inline constexpr bool invocable_with_int_v = invocable_with_int<F>::value;

template<bool B>
    struct my_enable_if {};

template<>
    struct my_enable_if<true>
        { using type = void; };

template <bool B>
    using my_enable_if_t = typename my_enable_if<B>::type;

问题仍然是同一个https://coliru.stacked-crooked.com/a/722a2041600799b0

代码语言:javascript
复制
main.cpp:29:73:   required by substitution of 'template<class F> std::true_type check(const F&, decltype (declval<F>()(1))*) [with F = CurryT<main()::<lambda(int, int, int)>, int>]'

它试图解析对此函数的调用:

代码语言:javascript
复制
template <typename F> true_type  check(const F &, decltype( declval<F>()(1) )* );

但是decltype (declval<F>()(1))*)失败了。但是,由于模板替换失败,这个函数不应该从重载解析中删除吗?当第一次调用Decouple时,它会工作。但是当它被称为第二次时,SFINAE似乎被禁用了,而模板替换的第一次失败会导致编译错误。二级SFINAE是否存在一定的局限性?为什么递归调用模板函数不起作用?

这个问题出现在GCC和Clang两人身上。所以它不是编译器的错误。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2022-05-09 14:36:04

您的operator()重载是完全不受约束的,因此声称可以使用任何参数集调用。只检查声明,而不是定义,以确定在重载解析中调用哪个函数。如果定义中的替换失败,则不适用SFINAE。

因此,约束您的operator()要求TFunc可以用TArgTArgs...作为参数进行调用。

例如:

代码语言:javascript
复制
template <typename... TArgs>
auto operator()(TArgs ...args) const -> decltype(func(arg, args...))
票数 9
EN

Stack Overflow用户

发布于 2022-05-09 15:12:26

对我来说,奇怪的是,您的CurryT::operator()接受未知数量的参数。

由于目的是要有一个只接受一个论点的函数,所以我期望这个函数只接受一个论点。

海事组织根据CurryT持有的函数类型,CurryT::operator()应该返回不同的类型:返回启动函数的类型或其他版本的CurryT

下面是我使用来自std::bind_front的C++20的方法:

代码语言:javascript
复制
namespace detail {
template <typename TFunc>
class CurryT
{
public:
    explicit CurryT(TFunc f) : mF(std::move(f))
    {}

    template<typename T>
    auto get(T&& x, int = 0) -> decltype(std::declval<TFunc>()(x)) {
        return mF(x);
    }

    template<typename T>
    auto get(T&& x, char) {
        return CurryT<decltype(std::bind_front(mF, std::forward<T>(x)))>{
            std::bind_front(mF, std::forward<T>(x))
        };
    }

    template<typename T>
    auto operator()(T&& x)
    {
        return this->get(std::forward<T>(x), 1);
    }
private:
     TFunc mF;
};
}

template<typename F>
auto Decouple(F&& f)
{
    return detail::CurryT<std::decay_t<F>>{std::forward<F>(f)};
}

https://godbolt.org/z/eW9r4Y6Ea

注意,使用这种方法,整数参数不会强制执行,就像在解决方案中一样。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/72173127

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档