【问题标题】:Applying a function to each element of a tuple将函数应用于元组的每个元素
【发布时间】:2017-11-07 09:11:54
【问题描述】:

给定一个类似std::tuple 的对象(即定义了tuple_size 和get 语义)和一个一元函子对象ftor,我希望能够在@987654328 的每个元素上调用ftor @-like 对象。

如果我忽略返回值,我知道 int 数组技巧:

namespace details {

template <typename Ftor, typename Tuple, size_t... Is>
void apply_unary(Ftor&& ftor, Tuple&& tuple, std::index_sequence<Is...>) {
    using std::get;
    int arr[] = { (ftor(get<Is>(std::forward<Tuple>(tuple))), void(), 0)... };
}

} // namespace details

template <typename Ftor, typename Tuple>
void apply_unary(Ftor&& ftor, Tuple&& tuple) {
    details::apply_unary(std::forward<Ftor>(ftor),
                         std::forward<Tuple>(tuple),
                         std::make_index_sequence<std::tuple_size<Tuple>::value> {});
}

如果我想要返回值,我可以将 int [] 技巧替换为对 std::make_tuple 的调用并返回它。如果对ftor 对象的调用都没有void 返回值,那么这将起作用...

因此,我的问题是:考虑到我想获得调用的结果,我该如何处理可能返回 void 的调用?

唯一的要求是我应该以元组的形式获得结果,并且能够判断哪个调用导致所述结果元组的哪个元素。

【问题讨论】:

  • I threw something together 使用 Boost Hana 基本上使用 hana::tuple 完成此操作,但我认为它不太值得回答。只是在这里发布,以防有​​人想将其改编为答案
  • 您可以构建一个映射模板,在元组项索引和结果元组项索引之间进行映射。
  • 将仿函数调用包装在一个函数中,该函数在 void 情况下返回 dummy struct 而不是 void
  • @lorro 不是这样,在您链接的问题中,返回值被丢弃。如果有的话,您链接的问题可以被视为我的问题的子案例

标签: c++ tuples c++14


【解决方案1】:

正如@Jarod42 所建议的,用一个额外的层来包装调用,该层负责用虚拟结构替换 void return 就可以了:

struct no_return {};

namespace details {

template <typename Ftor, typename Arg>
auto call(Ftor&& ftor, Arg&& arg)
    -> std::enable_if_t<std::is_void<decltype(std::forward<Ftor>(ftor)(std::forward<Arg>(arg)))>::value, no_return> {
    std::forward<Ftor>(ftor)(std::forward<Arg>(arg));
    return no_return {};
}

template <typename Ftor, typename Arg>
auto call(Ftor&& ftor, Arg&& arg)
    -> std::enable_if_t<!std::is_void<decltype(std::forward<Ftor>(ftor)(std::forward<Arg>(arg)))>::value, decltype(std::forward<Ftor>(ftor)(std::forward<Arg>(arg)))> {
    return std::forward<Ftor>(ftor)(std::forward<Arg>(arg));
}

template <typename Ftor, typename Tuple, size_t... Is>
auto apply_unary(Ftor&& ftor, Tuple&& tuple, std::index_sequence<Is...>) {
    using std::get;
    return std::tuple<decltype(call(ftor, get<Is>(std::forward<Tuple>(tuple))))...> { call(ftor, get<Is>(std::forward<Tuple>(tuple)))... } ;
}

} // namespace details

template <typename Ftor, typename Tuple>
auto apply_unary(Ftor&& ftor, Tuple&& tuple) {
    return details::apply_unary(std::forward<Ftor>(ftor),
                                std::forward<Tuple>(tuple),
                                std::make_index_sequence<std::tuple_size<std::decay_t<Tuple> >::value> {});
}

Coliru 上提供现场演示

我使用 SFINAE 来区分这两个重载。它看起来有点难看,所以如果你有任何改进建议......我全都听好了!

【讨论】:

  • std::invoke_result 可能比decltype 短。 typename Ret = /**/ 可能会减少一点重复。
  • 注意:如果要保证评估顺序,make_tuple应该替换为tuple&lt;..&gt;{..}。
  • 正式注明@Jarod42。我保留了decltype 以坚持使用 C++14
  • @Jarod42: std::invoke_result 不幸的是 C++17 及更高版本。 @Rerito,您可以使用 std::is_void 而不是 std::is_same&lt;void, T&gt; 来缩短它
【解决方案2】:

另一种方式:

namespace details {

struct apply_unary_helper_t {};

template<class T> 
T&& operator,(T&& t, apply_unary_helper_t) { // Keep the non-void result.
    return std::forward<T>(t); 
}

template <typename Ftor, typename Tuple, size_t... Is>
void apply_unary(Ftor&& ftor, Tuple&& tuple, std::index_sequence<Is...>) {
    auto r = {(ftor(std::get<Is>(std::forward<Tuple>(tuple))), apply_unary_helper_t{})...};
    static_cast<void>(r); // Suppress unused variable warning.
}

} // namespace details

template <typename Ftor, typename Tuple>
void apply_unary(Ftor&& ftor, Tuple&& tuple) {
    details::apply_unary(std::forward<Ftor>(ftor),
                         std::forward<Tuple>(tuple),
                         std::make_index_sequence<std::tuple_size<std::remove_reference_t<Tuple>>::value> {});
}

在上面,它将operator, 应用于ftor 和apply_unary_helper_t 的结果。如果ftor的结果是void,那么r就是std::initializer_list&lt;details::apply_unary_helper_t&gt;,否则r就是std::initializer_list&lt;decltype(ftor(...))&gt;,你可以利用。

【讨论】:

  • operator, 重载滥用的精彩演示 :)
  • @Rerito 称之为过度滥用就像称之为任何不必要的求爱骚扰。
  • 我不是这个意思!
  • @Rerito 我知道,我注意到了笑脸。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-01-21
  • 2021-05-25
  • 2016-08-19
  • 1970-01-01
  • 2014-09-24
  • 2015-09-09
相关资源
最近更新 更多