【问题标题】:Forwarding wrapper using `std::enable_if` failing使用 `std::enable_if` 转发包装器失败
【发布时间】:2019-11-03 02:19:53
【问题描述】:

我正在尝试创建一个转发包装函数,该函数对 中的函数调用进行计时。我需要处理 2 类型,

  • 一个正在计时一个不返回值的函数,并且

  • 其他未返回

在此处跳过的调用前后可能需要执行一些操作,因此我不能只返回func(),它同时处理voidnon-void 类型。

这是void 函数的包装器。

template<typename T, typename ...U>
auto time_function(T&& func, U&& ...args) -> typename enable_if<is_same<decltype(func(args...)), void>::value>::type
{
    std::cout << "timing void function" << std::endl;
    std::forward<T>(func)(std::forward<U>(args)...);
    std::cout << "timing over" << std::endl;
}

non-void 函数的包装器

template<typename T, typename ...U>
auto time_function(T&& func, U&& ...args) -> typename enable_if < !is_same<decltype(func(args...)), void), decltype(func(args...)) > ::value > ::type
{
    std::cout << "timing returning function" << std::endl;
    auto val = std::forward<T>(func)(std::forward<U>(args)...);
    std::cout << "timing over" << std::endl;
    return val;
}

int main()
{
    time_function(foo, 2);
    int i = time_function(&foo_return, 1); //this generates an error
    //std::cout<<i<<std::endl;
}

foo 是一个返回void 的函数,foo_return 返回一个整数。 产生的错误是

<source > :28 : 129 : error : template argument 1 is invalid
auto time_function(T && func, U && ...args) -> typename enable_if<!is_same<decltype(func(args...)), void>, decltype(func(args...))>::value > ::type
^
<source>:28 : 55 : error : expected nested - name - specifier before 'enable_if'
auto time_function(T && func, U && ...args) -> typename enable_if<!is_same<decltype(func(args...)), void>, decltype(func(args...))>::value > ::type
^ ~~~~~~~~
<source>:28 : 129 : error : template argument 1 is invalid
auto time_function(T && func, U && ...args) -> typename enable_if<!is_same<decltype(func(args...)), void>, decltype(func(args...))>::value > ::type
^
<source>:28 : 129 : error : template argument 1 is invalid
<source> : 28 : 129 : error : template argument 1 is invalid
<source> : 28 : 55 : error : expected initializer before 'enable_if'
auto time_function(T && func, U && ...args) -> typename enable_if<!is_same<decltype(func(args...)), void>, decltype(func(args...))>::value > ::type
^ ~~~~~~~~

<source>: In function 'int main()' :
    <source> : 42 : 41 : error : no matching function for call to 'time_function(int (*)(int), int)'
    int i = time_function(&foo_return, 1); //error -
^
<source>:20 : 6 : note : candidate : template<class T, class ... U> typename std::enable_if<std::is_same<decltype (func(time_function::args ...)), void>::value>::type time_function(T&&, U && ...)
auto time_function(T && func, U && ...args) -> typename enable_if<is_same<decltype(func(args...)), void>::value>::type
^ ~~~~~~~~~~~~
<source> : 20 : 6 : note : template argument deduction / substitution failed :
<source> : In substitution of 'template<class T, class ... U> typename std::enable_if<std::is_same<decltype (func(time_function::args ...)), void>::value>::type time_function(T&&, U&& ...) [with T = int (*)(int); U = {int}]' :
    <source> : 42 : 41 : required from here
    <source> : 20 : 6 : error : no type named 'type' in 'struct std::enable_if<false, void>'

据我所知,包装是正确的,有什么问题?我正在使用is_same 检查函数的返回类型是否为void,如果是,则声明我希望使用enable_if 的返回类型。

【问题讨论】:

  • 我会避免在decltype 中使用的f(args...) 和函数中实际调用的std::forward&lt;T&gt;(func)(std::forward&lt;U&gt;(args)...) 之间存在差异。您可能有一个有效的和一个无效的,或者它们可能是两个不同的函数,其中一个返回 void 而另一个不返回。
  • @aschepler 当我进行转发时,它只是执行函数的条件静态转换,对吗?怎么可能是2个不同的功能或无效?
  • 例如,给定void f1(std::string&amp;&amp;); 并尝试执行time_function(f1, std::string{}); 然后f1(arg) 无效但f1(std::forward&lt;std::string&gt;(arg)) 有效。给定void f2(std::string&amp;);,则f2(arg) 有效,但f2(std::forward&lt;std::string&gt;(arg)) 无效。如果f3 有一个具有void operator()(std::string&amp;);int operator()(std::string&amp;&amp;); 的类类型,那么enable_if 会看到返回类型void,但主体调用返回类型为int 的函数。
  • 我理解将args转发给函数,但是函数本身呢?

标签: c++14 c++ templates c++14 sfinae function-templates


【解决方案1】:

@rmawatson 已经指出语法错误。不过,我想提几个改进。

由于您使用的是,因此您可以使用std::enable_if 的简洁版本,通过使用helper type std::enable_if_t 来实现相同的效果

其次,std::is_void 是标准中用于检查类型是否为void 的更好(或合适)特征,这也将节省一些输入。使用variable templates,(也因为),那会更短! (See here a live demo)

#include <iostream>
#include <type_traits> // std::enable_if_t, std::is_void
// variable templates
template<class T> constexpr bool is_void_v = std::is_void<T>::value;

template<typename T, typename ...U>
auto time_function(T&& func, U&& ...args) -> std::enable_if_t<::is_void_v<decltype(func(args...))>>
{
    // ... code here
}

template<typename T, typename ...U>
auto time_function(T&& func, U&& ...args)
-> std::enable_if_t<!::is_void_v<decltype(func(args...))>, decltype(func(args...))>
{
    // ... code here
}

但是,如果您有权访问 ,则可以使用 if constexpr 在一个函数中简单地编写这两个逻辑。只是为了将来的 @todo 列表?。 (See live here)

#include <type_traits> // std::enable_if_t, std::is_void_v

template<typename T, typename ...U>
auto time_function(T&& func, U&& ...args)
{
    if constexpr (std::is_void_v<decltype(func(args...))>)
    {
        // ... timing void function"
    } 
    else
    {
        // ... timing returning function
    }
}

【讨论】:

  • 不错!为什么我的行 auto time_function(T&& func, U&& ...args) -> typename enable_if_t::value> 给出错误 - 预期的嵌套名称说明符?
  • @SridharThiagarajan 您正在使用辅助类型enable_if_t 意思是,您不需要额外使用typename::type!更改为-&gt; typename enable_if&lt;is_void&lt;decltype(func(args...))&gt;::value&gt; 或像我在答案-&gt; enable_if_t&lt;is_void&lt;decltype(func(args...))&gt;&gt; 中提到的那样。看到类似的问题:stackoverflow.com/questions/6489351/nested-name-specifier
【解决方案2】:

你有一个语法错误,

-&gt; typename enable_if&lt;!is_same&lt;decltype(func(args...)), void),decltype(func(args...))&gt;::value&gt;::type

-&gt; typename enable_if&lt;!is_same&lt;decltype(func(args...)), void&gt;::value, decltype(func(args...))&gt;::type

#include <type_traits>
#include <iostream>
using namespace std;

template<typename T, typename ...U>
 auto time_function(T&& func, U&& ...args) 
    -> typename enable_if<is_same<decltype(func(args...)), void>::value>::type
{
    std::cout<<"timing void function"<<std::endl;
    std::forward<T>(func)(std::forward<U>(args)...);

    std::cout<<"timing over"<<std::endl;
}

template<typename T, typename ...U>
auto time_function(T&& func, U&& ...args)
 -> typename enable_if<!is_same<decltype(func(args...)), void>::value, decltype(func(args...))>::type
{
    std::cout<<"timing returning function"<<std::endl;
    auto val = std::forward<T>(func)(std::forward<U>(args)...);

    std::cout<<"timing over"<<std::endl;

    return val;
}


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

int main()
{

    time_function(foo, 2);
    int i = time_function(&foo_return, 1); //this generates an error
    std::cout<<i<<std::endl;

}

Demo

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-08-29
    • 2021-11-10
    • 1970-01-01
    • 2017-09-18
    • 2016-04-27
    • 2020-12-25
    • 2021-05-25
    相关资源
    最近更新 更多