【问题标题】:Functional composition with variadic templates具有可变参数模板的功能组合
【发布时间】:2016-06-27 20:45:01
【问题描述】:

我的目标是让函数组合使用这种精确语法:

int main() {
    Function<std::string, int> f([](const std::string& s) {return s.length();});
    Function<int, double> g([](int x) {return x + 0.5;});
    Function<double, int> h([](double d) {return int(d+1);});
    std::cout << compose(g, f, "hello") << '\n';  // g(f("hello")) = 5.5
    std::cout << compose(h, g, f, "hello") << '\n';  // h(g(f("hello"))) = 6
}

通过稍微改变语法,让 "hello" 参数先出现,我可以轻松地使用以下代码:

#include <iostream>
#include <functional>
#include <tuple>
#include <string>

template <typename D, typename R>
struct Function {
    using domain = const D&;
    using range = R;
    using function = std::function<range(domain)>;
    const function& f;
    Function (const function& f) : f(f) {}
    range operator()(domain x) const {return f(x);}
};

template <typename... Ts>
struct LastType {
    using Tuple = std::tuple<Ts...>;
    using type = typename std::tuple_element<std::tuple_size<Tuple>::value - 1, Tuple>::type;
};

template <typename F, typename G>
typename G::range compose (const typename F::domain& x, const G& g, const F& f) {
    return g(f(x));
}

template <typename F, typename... Rest>
auto compose (const typename LastType<Rest...>::type::domain& x, const F& f, const Rest&... rest) {
    return f(compose(x, rest...));
}

int main() {
    Function<std::string, int> f([](const std::string& s) {return s.length();});
    Function<int, double> g([](int x) {return x + 0.5;});
    Function<double, int> h([](double d) {return int(d+1);});
    std::cout << compose("hello", g, f) << '\n';  // g(f("hello")) = 5.5
    std::cout << compose("hello", h, g, f) << '\n';  // h(g(f("hello"))) = 6
}

完成之后,我认为修改上述代码以便获得我想要的确切语法(即“hello”位于列表末尾)将是一项微不足道的任务,但它变得更加困难比我想象的要好。我尝试了以下方法,但无法编译:

#include <iostream>
#include <functional>
#include <tuple>
#include <string>

template <typename D, typename R>
struct Function {
    using domain = const D&;
    using range = R;
    using function = std::function<range(domain)>;
    const function& f;
    Function (const function& f) : f(f) {}
    range operator()(domain x) const {return f(x);}
};

template <typename F, typename G>
typename G::range compose (const G& g, const F& f, const typename F::domain& x) {
    return g(f(x));
}

template <typename F, typename... Rest>
auto compose (const F& f, const Rest&... rest) {
    return f(compose(rest...));
}

int main() {
    Function<std::string, int> f([](const std::string& s) {return s.length();});
    Function<int, double> g([](int x) {return x + 0.5;});
    Function<double, int> h([](double d) {return int(d+1);});
    std::cout << compose(g, f, "hello") << '\n';  // g(f("hello")) = 5.5
    std::cout << compose(h, g, f, "hello") << '\n';  // h(g(f("hello"))) = 6
}

而且我不知道如何解决它。谁能帮我解决这个问题?

我想出的一个新想法是定义compose_,它将重新排序args... 的参数(通过一些std::tuple 操作),以便第一个元素放在最后,然后将该参数包传递给compose。不过这看起来很混乱,即使它有效,也必须有一个更直接(更短)的解决方案。

【问题讨论】:

  • 或许this ?
  • @Piotr Skotnicki 太棒了!我试图弄清楚为什么你的编译为什么我的不编译。

标签: c++ c++11 templates variadic-templates variadic


【解决方案1】:

看起来以下方法也有效:

template <typename T>
const T& compose (const T& t) {
    return t;
}

template <typename F, typename... Rest>
typename F::range compose(const F& f, Rest... rest) {
    return f(compose(rest...));
}

【讨论】:

  • 比我的解决方案更简单。不错。
【解决方案2】:

这样怎么样?

#include <iostream>    
#include <functional>
#include <tuple>
#include <string>

template <typename D, typename R>
struct Function {
    using domain = const D&;
    using range = R;
    using function = std::function<range(domain)>;
    const function& f;
    Function (const function& f) : f(f) {}
    range operator()(domain x) const {return f(x);}
};

template <typename F, typename X = typename F::domain>
typename F::range compose (const F& f, const X & x) {
    return f(x);
}

template <typename F, typename... Rest>
typename F::range  compose (const F& f, const Rest&... rest) {
    return f(compose(rest...));
}

int main() {
    Function<std::string, int> f([](const std::string& s) {return    s.length();});
    Function<int, double> g([](int x) {return x + 0.5;});
    Function<double, int> h([](double d) {return int(d+1);});
    std::cout << compose(g, f, "hello") << '\n';  // g(f("hello")) = 5.5
    std::cout << compose(h, g, f, "hello") << '\n';  // h(g(f("hello"))) = 6
}

您只能在 c++14 中将auto 用于compose() 的返回类型(如果我没记错的话)。

您的版本无法编译,因为您的 compose() 的可变参数版本使用 N 个可变参数类型和 N 个参数,而最终(非可变参数)使用 2 个类型和 3 个参数。换句话说,可变参数版本丢失了最后一个参数。 您的版本无法编译,因为最终(不是可变参数版本)从未使用过:编译器选择可变参数版本。添加typename X = typename F::domain(并将const typename F::domain&amp; 更改为const X&amp;)首选最终版本,并且您的代码应该编译(至少使用c ++ 14)[由Piotr Skotnicki更正;谢谢]

ps:对不起,我的英语不好。

【讨论】:

  • "您的版本无法编译,因为 compose() 的可变参数版本具有 N 个可变参数类型和 N 个参数,而最终(非可变参数)具有 2 个可变参数类型和 3 个参数。 ”,我认为 OP 的代码无法编译,因为无论Rest 推断什么,与const typename F::domain&amp; 相比,它都是完全匹配的
  • @Piotr:你说得对:我的解释是错误的。
  • @Piotr:是的,你的权利:将 OP 代码的可变参数版本的模板定义从 template &lt;typename G, typename F,&gt; 更改为 template &lt;typename G, typename F, typename X = typename F::domain&gt;,OP 的代码编译
  • typename X = typename F::domain 没有多大意义,因为无论如何都会推导出X
  • @Piotr:我不确定(我不是真正的专家),但我认为问题(在 Prestokey 代码中)是 compose() 的最终(非可变参数)版本是预期的接收作为最后一个参数的type::domain(在示例中)a` std::string const`。 (继续)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-07-07
  • 2014-07-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-07
  • 1970-01-01
相关资源
最近更新 更多