【问题标题】:How to specialize template function to distinguish void and non-void function arguments如何专门化模板函数以区分 void 和非 void 函数参数
【发布时间】:2020-04-02 14:19:43
【问题描述】:

我想要两个函数:接受void(*)(int)int(*)(int)。 如何编写类似于 std::is_invocable_r 的 type_trait 但检查确切的返回类型(因为任何函数都可以转换为返回 void 的函数)。

#include <functional>
#include <type_traits>
#include <cstdio>

template <typename R, typename C, typename... Args>
constexpr bool is_exact_invocable_r_v =
        std::is_same_v<R, std::invoke_result_t<C, Args...>>;
        // std::is_invocable_r_v<R, C, Args...>;

template<typename C, std::enable_if_t<is_exact_invocable_r_v<int, C>, int> = 0>
void print(C)
{
    printf("1\n");
}

template<typename C, std::enable_if_t<is_exact_invocable_r_v<int, C, int>, int> = 0>
void print(C)
{
    printf("2\n");
}

template<typename C, std::enable_if_t<is_exact_invocable_r_v<void, C, int>, int> = 0>
void print(C)
{
    printf("3\n");
}

int main()
{
    print([](){return 0;});
    print([](int){return 0;});
    print([](int){});
}

std::is_invocable_r_v&lt;R, C, Args...&gt; 会导致歧义,因为任何类型都可以强制转换为 void。

std::is_same_v&lt;R, std::invoke_result_t&lt;C, Args...&gt;&gt; 导致替换失败。

【问题讨论】:

  • 澄清一下:short(*)(int) 不应该匹配任何重载? IOW, std::is_same_v&lt;decltype(C(0)), int&gt; ?
  • 另外,std::is_same_v 不是 C++11 的一部分
  • 请编辑以添加您从该程序获得的确切错误消息。
  • 这与将结果强制转换为 void 无关。问题是您正在混合“这是调用我的可调用对象的有效方法”和“返回类型是什么”的步骤。第一步是 SFINAE 友好的,第二步不是。仅当is_invokable 不产生替换失败时,您才需要有条件地检查invoke_result
  • 或者换一种说法,SFINAE 可以消除导致使用不同特化或重载的声明,但您只有一个模板声明,如果 invoke_result_t是非法的。

标签: c++ c++17 sfinae


【解决方案1】:

问题是当 C 不能用 Args... 调用时,你的特征是不正确的,你必须 SFINAE 你的变量:

template <typename R, typename C, typename TupleArgs, typename Enabler = void>
constexpr bool is_exact_invocable_r_v_impl = false;

template <typename R, typename C, typename... Args>
constexpr bool is_exact_invocable_r_v_impl<R,
                                          C,
                                          std::tuple<Args...>,
                                          std::void_t<std::invoke_result_t<C, Args...>>> =
    std::is_same_v<R, std::invoke_result_t<C, Args...>>;

template <typename R, typename C, typename... Args>
constexpr bool is_exact_invocable_r_v =
    is_exact_invocable_r_v_impl<R, C, std::tuple<Args...>>;

Demo

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-02
    • 1970-01-01
    • 2016-05-03
    • 2015-04-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多