我正在尝试学习可变模板函数,所以我尝试制作一个函数“timeIt”,它接受另一个函数并调用它。
#include <chrono>
#include <iostream>
#include <string>
std::string leftPad(std::string str, unsigned len, char c = ' ') {
if (str.length() >= len)
return str;
return std::string(len - str.length(), c) + str;
}
template <typename T, typename... Args, typename... Args2>
auto timeIt(T (*func)(Args...), Args2... args) {
namespace ch = std::chrono;
auto start = ch::high_resolution_clock::now();
func(args...);
auto end = ch::high_resolution_clock::now();
return ch::duration_cast<ch::microseconds>(end - start).count();
}
int main() {
std::cout << timeIt(&leftPad, "foo", 10) << std::endl;
return 0;
}
只有在我将最后一个参数显式传递为“”时才会编译,如
timeIt(&leftPad, "foo", 10, ' ')
在这种情况下有没有办法使用默认参数?
不需要自己做繁重的工作,这已经可以通过像这样的 lambda 函数来完成(例如通过引用捕获的 foo):
#include <chrono>
#include <iostream>
#include <string>
std::string leftPad(std::string str, unsigned len, char c = ' ') {
if (str.length() >= len)
return str;
return std::string(len - str.length(), c) + str;
}
// no need to do all the argument capturing for timing
// leave that to lambda functions
template <typename fn_t>
auto timeIt(fn_t fn) {
namespace ch = std::chrono;
auto start = ch::high_resolution_clock::now();
fn();
auto end = ch::high_resolution_clock::now();
return ch::duration_cast<ch::microseconds>(end - start).count();
}
int main() {
// use a lambda function
std::string foo{ "foo" };
std::cout << timeIt([&] { leftPad(foo, 10); }) << std::endl;
return 0;
}
您可以传递一个 lambda 来调用您的函数:
template <typename T, typename... Args2>
auto timeIt(T func, Args2... args) {
//...
func(args...);
//...
}
std::cout << timeIt([](auto... args){ return leftPad(args...); }, "foo", 10) << std::endl;
一般来说,你应该使用完美转发,即
Args2&&
代替 Args2
,auto&&
代替 auto
,等等,然后在任何使用参数的地方用 std::forward
转发:
template <typename T, typename... Args2>
auto timeIt(T func, Args2... args) {
//...
func(std::forward<Args2>(args)...);
//...
}
std::cout << timeIt([](auto&&... args){ return leftPad(std::forward<decltype(args)>(args)...); }, "foo", 10) << std::endl;
默认参数值不是函数签名的一部分。
leftPad
的签名就是:
std::string (std::string, unsigned, char)
函数声明中的默认参数值只允许任何直接调用该函数的调用点省略参数,编译器将在调用点用它的默认值填充参数,例如:
leftPad("foo", 10)
被称为好像它被写成:
leftPad("foo", 10, ' ')
但是,在您的
timeIt()
函数中,调用者传递一个指向函数的指针,有关该函数中任何默认参数值的所有信息都将丢失,因为它们不是 func
是的函数签名的一部分指向。因此,为什么 all 参数值必须显式传递给 timeIt()
。
正如其他答案所证明的那样,对
func
使用lambda会将leftPad
的call site移动到编译器能够按预期应用默认值的位置。