在c++中,可变参数函数内部调用带有默认参数的函数

qq24tv8q  于 2023-05-02  发布在  其他
关注(0)|答案(3)|浏览(127)

我正在尝试学习变元函数,所以我尝试创建一个函数“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, ' ')

在这种情况下,有没有办法使用默认参数?

x4shl7ld

x4shl7ld1#

你可以传递一个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&&而不是Args2auto&&而不是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;
x6492ojm

x6492ojm2#

默认参数值不是函数签名的一部分。leftPad的签名是:
std::string (std::string, unsigned, char)
函数声明中的默认参数值只是允许任何 * 直接 * 调用该函数的 * 调用站点 * 忽略该参数,编译器将在调用站点 * 使用其默认值 * 填充参数,例如:
leftPad("foo", 10)
被称为 as-if,它被写为:
leftPad("foo", 10, ' ')
当 * 间接地 * 调用函数时,情况并非如此。在timeIt()函数中,调用者向func传递了一个指向函数的 * 指针 *。所有关于指定函数中的默认参数值的信息都将丢失,因为它们不是函数签名的一部分。因此,all 参数值必须显式地传递给timeIt(),这样它们才能转发给func
在你的例子中,func的类型被推导为:
std::string (*func)(std::string, unsigned, char)
因此,调用者需要显式传入' '作为最后一个参数。
正如其他答案所展示的,对func使用lambda将把leftPad的 * 调用位置 * 移动到 direct 调用中,编译器能够按照预期应用默认值,例如:
timeIt([&] { leftPad(foo, 10); })
被称为 as-if,它被写为:
timeIt([&] { leftPad(foo, 10, ' '); })

sr4lhrrt

sr4lhrrt3#

不需要自己做繁重的工作,这已经可以通过像这样的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;
}

相关问题