【问题标题】:How to extend std::apply to work on non tuple types?如何扩展 std::apply 以处理非元组类型?
【发布时间】:2017-05-16 13:23:40
【问题描述】:

我有一种情况,我需要将输入参数应用于函数而不关心它是否是元组。如果是元组,需要解包,所以不需要函数参数检测。

这是我尝试过的

template <typename Callable, typename Tuple>
auto geniune_apply(Callable&& callable, Tuple&& tuple)
{
    return std::apply(std::forward<Callable>(callable), std::forward<Tuple>(tuple));
}

template <typename Callable, typename T, typename = typename std::enable_if<!shino::is_tuple_like<std::decay_t<T>>::value>::type>
auto geniune_apply(Callable&& callable, T&& arg)
{
    return std::forward<Callable>(callable)(std::forward<T>(arg));
}

这会导致模棱两可,这正是我的预期。然后我尝试对元组的大小进行 SFINAE,但我无法防止非元组类型的编译错误。

这是我正在使用的测试用例

#include <cassert>
#include <iostream>
#include <stdexcept>
#include <vector>

int dummy_x(const std::tuple<int, int>&)
{
    return 1;
}

int dummy_y(int y)
{
    return y;
}

int main()
{
    shino::geniune_apply(&dummy_x, std::tuple<int, int>(1, 1));
    shino::geniune_apply(dummy_y, 1);
    shino::geniune_apply(dummy_y, std::make_tuple(1));
}

如果需要,类似元组的代码。它基本上测试它是std::array还是std::tuple

template <typename T>
    struct is_straight_tuple
    {
        static constexpr bool value = false;

        constexpr operator bool()
        {
            return value;
        }
    };

    template <typename ... Ts>
    struct is_straight_tuple<std::tuple<Ts...>>
    {
        static constexpr bool value = true;

        constexpr operator bool()
        {
            return value;
        }
    };

    template <typename T>
    struct is_std_array
    {
        static constexpr bool value = false;
    };

    template <typename T, std::size_t size>
    struct is_std_array<std::array<T, size>>
    {
        static constexpr bool value = true;

        constexpr operator bool()
        {
            return value;
        }
    };

    template <typename T>
    struct is_tuple_like
    {
        static constexpr bool value = is_std_array<T>::value || is_straight_tuple<T>::value;

        constexpr operator bool()
        {
            return value;
        }
    };

【问题讨论】:

  • std::apply 是 C++17,但我不知道如何启用 if constexpr,所以我决定将问题标记为 C++14

标签: c++ c++14 template-meta-programming overload-resolution


【解决方案1】:

在 C++14 中解决这个问题的最简单方法是使用标签调度:

template <typename Callable, typename Arg>
decltype(auto) geniune_apply(Callable&& callable, Arg&& arg)
{
    return details::genuine_apply(std::forward<Callable>(callable),
         std::forward<Arg>(arg),
         is_tuple_like<std::decay_t<Arg>>{});
}

将您的is_tuple_like 更改为从std::integral_constant&lt;bool, ???&gt; 继承,而不是重新实现相同的。这将允许您编写这两个辅助函数:

namespace details {
    // the tuple-like case
    template <typename Callable, typename Tuple>
    decltype(auto) genuine_apply(Callable&&, Tuple&&, std::true_type );

    // the non-tuple-like case
    template <typename Callable, typename Arg>
    decltype(auto) genuine_apply(Callable&&, Arg&&, std::false_type );
}

在 C++17 中,更好的解决方案是简单地使用 if constexpr 而不是标签调度。使用 C++ 概念,您解决问题的初始方法实际上可以按原样工作(有一个不受约束的函数模板,一个受第二个参数约束的类似元组的函数模板)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-11-29
    • 1970-01-01
    • 1970-01-01
    • 2022-12-03
    • 1970-01-01
    • 2013-12-01
    • 2021-06-05
    • 2016-01-29
    相关资源
    最近更新 更多