【问题标题】:std::result_of failing for void return typestd::result_of 因 void 返回类型而失败
【发布时间】:2017-02-13 07:47:42
【问题描述】:

我正在尝试使用 SFINAE 创建函数调用包装器,使用 std::result_of 来获取函数的返回类型。重现问题的小样本:

void test(int) {}

template<typename T, typename... Args, typename R = typename std::result_of<T(Args...)>::type, typename std::enable_if<!std::is_void<R>::value, int>::type* = nullptr>
int test2(R& ret_param, Args... args)
{
    T* test_call = test;
    ret_param = test_call(args...);
    return 0;   
}

template<typename T, typename... Args, typename std::enable_if<std::is_void<typename std::result_of<T(Args...)>::type>::value, int>::type* = nullptr>
int test2(Args... args)
{
    T* test_call = test;
    test_call(args...);
    return 0;   
}

int main()
{
  test2<decltype(test)>(1);
}

用 gcc 4.9.2 编译它会导致:

68:26: error: no matching function for call to 'test2(int)'
68:26: note: candidates are:
46:5: note: int test2(R&, Args ...) [with T = void(int); Args = {}; R = int; typename std::enable_if<(! std::is_void<R>::value), int>::type* <anonymous> = 0u]
46:5: note:   no known conversion for argument 1 from 'int' to 'int&'
56:5: note: template<class T, class ... Args, typename std::enable_if<std::is_void<typename std::result_of<_Functor(_ArgTypes ...)>::type>::value, int>::type* <anonymous> > int test2(Args ...)
56:5: note:   template argument deduction/substitution failed:
55:142: error: function returning a function
55:142: note: invalid template non-type parameter

那么问题是“typename std::result_of::type”以某种方式计算为返回函数的函数?如果模板函数的返回类型为 void,使用 SFINAE 强制重载决议选择不同函数的正确方法是什么?值得一提的是,在测试函数返回 int 的相反情况下。此外,如果为 std::is_void 解析删除了 enable_if ,它将在这种情况下工作(但显然,如果有返回类型,那么这两个函数都是有效的解析,并且当它选择不期望返回值的那个时编译将失败)。

【问题讨论】:

  • 首先,decltype(test) 是一个函数类型,您将其绑定到 T,在启用逻辑中它被视为返回类型。
  • 我会使用class 而不是typename,我会避免使用那些数百个字符的行。尝试自然地拆分内容以便于阅读。
  • 不跟随,我的理解是 std::result_of::type 是函数 T 的返回类型,如果使用参数 Args... 调用它是我在启用逻辑中使用的内容。
  • T(Args...),例如int(char, double),是一个返回 T 并带有形式参数 Args... 的函数。等一下。 std::result_of 被定义为期望这一点。对不起。
  • 好吧,对不起“对不起”:您需要形成 T_ref(Args...),其中 T_ref 是 T&。请参阅std::result_of 要求。

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


【解决方案1】:

如果您接受将测试函数作为参数传递,则不需要所有样板:

void test(int) {}
int test(double) { return 0; }

template<typename R, typename... Args>
int test2(R(&f)(Args...), R& ret_param, Args... args)
{
    ret_param = f(args...);
    return 0;   
}

template<typename... Args>
int test2(void(&f)(Args...), Args... args)
{
    f(args...);
    return 0;   
}

int main()
{
    test2(test, 1);
    int r;
    test2(test, r, .0);
}

扣除和超载会为您完成工作。

【讨论】:

  • 感谢您的回答,不幸的是我无法将函数作为参数传递,因为我只有函数的声明。传递实际函数只会给我链接器错误。只能使用函数的decltype。
  • @Grahalt 好的,您在示例中添加了定义,所以我不知道。对不起。
【解决方案2】:

基本上你需要形成T_ref(Args...) 作为std::result_of 的参数。

以下编译。我应用了一些简化和重新格式化。

顺便说一句,我认为这不是一个好主意。相反,这两个函数只需不同的名称。毕竟,他们确实需要不同的论据。明确是好的。隐式不好。

#include <type_traits>      // std::(enable_if, result_of)

void test(int) {}
int foo() { return 0; }

template<
    class T,  class R, class... Args,
    class T_ref = T&,
    typename std::enable_if<
        !std::is_void<
            std::result_of_t<T_ref(Args...)>
            >::value
        >::type* = nullptr
    >
int test2(R& ret_param, Args... args)
{
    static_assert( std::is_same<
        R,
        std::result_of_t<T_ref(Args...)>
        >::value, "!" );
    T* test_call = foo;
    ret_param = test_call(args...);
    return 0;
}

template<
    class T, class... Args,
    class T_ref = T&,
    typename std::enable_if<
        std::is_void<
            std::result_of_t<T_ref(Args...)>
            >::value
        >::type* = nullptr
    >
int test2(Args... args)
{
    T* test_call = test;
    test_call(args...);
    return 0;   
}

int main()
{
    test2<decltype(test)>( 1 );

    int result;
    test2<decltype(foo)>( result );
}

【讨论】:

  • 普遍认为隐式是好的,而显式是坏的,但抽象地说它们在做同样的事情,所以我希望它们具有相同的名称,否则会使其他地方的一堆代码复杂化。感谢您的示例,是否有某种方法可以让我最初通过 ref 获取 T 并避免使用额外的模板参数?这很好用,只是想让它看起来比实际更漂亮:)
  • 你可以在result_of中写上T&amp;,而不需要添加额外的模板参数。
猜你喜欢
  • 1970-01-01
  • 2013-05-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-20
  • 2021-12-08
  • 1970-01-01
相关资源
最近更新 更多