如何在C++模板中使用void参数

3

我想编写一个函数来测试传入函数的运行时间。我使用模板使其适用于许多函数。

我省略了计时代码。就像这样:

template<typename F>
void get_run_time(F func)
{
//time start
 func;
//time end
}

但是,如果我传递的函数是void类型,它会报错并提示我添加F=void

我尝试添加它,但没有起作用。我可以将void更改为bool,但这非常奇怪。

另一个问题是我想测试一个函数的时间并正常运行整个代码。所以我增加了返回值。 像这样:

template<typename F>
F get_run_time(F func)
{
//time start
 F tf=func;
//time end
 return tf;
}

但实际测试时间显然是错误的。我猜测它在返回函数时开始运行。它如何在继续执行以下代码之前获得运行结果?


1
您没有调用函数。 - Swift - Friday Pie
我没有使用(),但它仍然有效。为什么? - jerx
@jerx 因为没有 ()func; 没有任何效果。 - Jason
1
@jerx 通过函数名引用函数只会产生一个指向该函数的指针。因此,你的语句 func; 实际上是一个空操作,它只评估该指针的值(指向的函数的地址),然后忽略结果。 - Remy Lebeau
2个回答

3
我认为,符合C++惯用方式的写法如下:
template <class F>
auto call(F f)
{
  Timer t;
  return f();
}

这适用于返回void的函数。注意,这里没有开始和停止。Timer是一个RAII类,它在构造时启动计时器,并在销毁时停止。

出于简洁起见,未显示将参数转发给f以及像std::invoke这样的优美操作。


2
我在想,这难道不是使用decltype(auto)作为返回类型的少数情况之一吗?这样可以完美地转发它。 - JHBonarius
@JHBonarius,坦白地说,我从来没有理解过它。 - n. m.
1
使用 auto 关键字,我们可以调用类型推断功能;而使用 decltype 关键字,则可以引入 decltype 推断规则。因此,call 函数将会返回与 f 函数完全相同的类型。 - rawrex
1
@rawrex 我们也可以使用原始的 auto call(F f) -> type-id 语法,其中 std::invoke_result_t<F, arg-types...> 作为 type-id,这将不依赖于返回语句,而是与调用代码无关。 - Swift - Friday Pie

2

首先,您需要调用传递的函数以实际计时其执行时间。请注意,在您的代码中,您没有调用它,使用()调用运算符:

template <typename Func>
void timer1 (Func func)
{
    // start timer
    func(); 
    // stop timer
}

其次,注意以下细节:

// Will take the same time as the timer1
template <typename Func>
Func timer2 (Func func1)
{
    // func2 is a copy of func1
    // This copying won't increase the time as you thought it will
    Func func2 = func1;
    // You still need to call the function
    func2();

    // Returns pointer to function
    return func2;
}

void foo() { std::cout << "foo()" << std::endl; }

int main() {
    // Func, here, is a type void (*)()
    // Prints "foo()"
    timer2(foo);    
}

第三个方法,您可能希望采用以下方式:

// Since C++14
auto timer3 = [](auto&& func, auto&&... args)
{
    // start timer

    // Forward the func and invoke the call operator
    // with however many forwarded arguments passed to the timer3
    std::forward<decltype(func)>(func)(std::forward<decltype(args)>(args)...);

    // stop timer
};

void foo(int, int) {}

int main() 
{
    timer3(foo, 21, 42);    
}

正如@JHBonarius指出的那样,更加正确和简洁的方法是使用std::invoke(自C++17起),这在此主题中得到了涵盖:什么是C++中的std::invoke?


1
请注意 std::invoke 的存在。cpp reference 页面也有很好的解释。 - JHBonarius

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接