C++ 在我知道可调用参数之前,如何约束惰性合成?

C++ 在我知道可调用参数之前,如何约束惰性合成?,c++,generic-programming,c++17,c++-concepts,C++,Generic Programming,C++17,C++ Concepts,因此,我正在研究GCC6及其概念实现,我认为Haskell Prelude将是一个很好的实验来源。Haskell的核心特性之一是函数组合,这是我需要马上解决的问题 我尽可能模仿Haskell语法,编写了以下函数: template <typename F, typename G> auto operator*(F f, G g) { return [f, g](auto... args) { return f(g(args...)); } } 很高兴,我决定回去对我

因此,我正在研究GCC6及其概念实现,我认为Haskell Prelude将是一个很好的实验来源。Haskell的核心特性之一是函数组合,这是我需要马上解决的问题

我尽可能模仿Haskell语法,编写了以下函数:

template <typename F, typename G>
auto operator*(F f, G g)
{
  return [f, g](auto... args) {
    return f(g(args...));
  }
}
很高兴,我决定回去对我的函数组合应用一些约束,但由于它的惰性,我很快遇到了一个问题

首先我写了这个概念:

template <typename F, typename Ret, typename... Args>
concept bool Function()
{
  return requires(F f, Args ...args) {
    { f(args...) } -> Ret;
  }
}

这是我能做的最好的了吗(如果我能做到的话)?

正如您所发现的,将约束放在正确的位置确实很重要。在您的例子中,必须约束的是结果的
操作符()
,而不是合成函数本身。您确实不能做得更好,例如,考虑到许多函数没有单一的返回类型(例如:代码> STD::MaMaGuple tub< /COD>)。然而,尽管Concepts Lite稍微涉及了lambda表达式,但它并没有允许在它们上使用
requires
子句,因此您的尝试将不起作用

在大多数情况下,我通常的建议是编写lambda表达式,以使生成的
操作符()
由于SFINAE而自然受到约束。在您的情况下,这意味着避免退货类型扣减:

return [f, g](auto... args) -> decltype( f(g(args...)) )
{ return f(g(args...)); }
如果您正在使用例如叮当声。如果使用GCC,您可能会在其中遇到错误

另一种方法是特意“展开”lambda表达式的闭包类型。通过将其设置为用户定义的类型,可以访问所有技巧,尤其是可以编写所需的显式约束:

template<typename F, typename G>
struct compose_type {
    F first_composed_function;
    G second_composed_function;

    template<typename... Args>
    constexpr auto operator()(Args... args)
        // substitute in whichever concepts and traits you're actually using
        requires
            Callable<G, Args...>
            && Callable<F, result_of<G, Args...>>
    { return first_composed_function(second_composed_function(args...)); }
};

template<typename F, typename G>
constexpr compose_type<F, G> compose(F f, G g)
{ return { std::move(f), std::move(g) }; }
模板
结构组合类型{
F第一个_复合函数;
G二次函数;
模板
constexpr自动运算符()(Args…Args)
//替换你实际使用的概念和特征
要求
可调用
&&可调用
{返回第一个组合函数(第二个组合函数(args…);}
};
模板
constexpr compose_type compose(F,G)
{return{std::move(f),std::move(g)};}

谢谢是的,这看起来是解决方案。这是一个遗憾,因为我不喜欢这样一个事实:我可以在没有概念错误的情况下组合两个不可调用的对象。我是否可以提供一个回调的可调用变体,它不需要预先输入参数类型?@SamKellett不是现成的。您可以选择遵守一个协议,其中函数对象必须使其“签名”或接近签名的内容可用,但这是一个巨大的任务,没有明显的好处。
return [f, g](auto... args) -> decltype( f(g(args...)) )
{ return f(g(args...)); }
template<typename F, typename G>
struct compose_type {
    F first_composed_function;
    G second_composed_function;

    template<typename... Args>
    constexpr auto operator()(Args... args)
        // substitute in whichever concepts and traits you're actually using
        requires
            Callable<G, Args...>
            && Callable<F, result_of<G, Args...>>
    { return first_composed_function(second_composed_function(args...)); }
};

template<typename F, typename G>
constexpr compose_type<F, G> compose(F f, G g)
{ return { std::move(f), std::move(g) }; }