通过跳过可选参数来填充参数包

Bra*_*don 1 c++ variadic-templates c++11

我正在编写一个用于日志记录的辅助函数:它收集调用站点信息,同时还创建一个Error类型对象。

template<typename ErrorTy, typename... ArgTys>
std::unique_ptr<Error> makeError(const char* fileName = __builtin_FILE(),
        int lineNum = __builtin_LINE(),
        const char* funcName = __builtin_FUNCTION(), ArgTys &&... args) {
    return std::make_unique<ContextualError<ErrorTy>>(fileName, lineNum, funcName,
            std::forward<ArgTys>(args)...);
}

// ...

template<typename ErrorTy>
struct ContextualError : ErrorTy {
    template<typename... ArgTys>
    ContextualError(std::string_view fileName, int lineNum,
            std::string_view funcName, ArgTys &&... args) :
            fileName_(fileName), lineNum_(lineNum), funcName_(funcName),
            ErrorTy(std::forward<ArgTys>(args)...) {}

    // ...
};
Run Code Online (Sandbox Code Playgroud)

由于实际的Error类可以有任意的构造函数,我希望得到makeErrorContextualError构造函数完美地转发所有内容。不幸的是,makeError像这样调用将尝试填充前三个可选参数,而不是跳过它们并填充参数包:

auto err = makeError<FileError>("foo.exe", "Invalid header");

// error: no matching function for call to 'makeError'
// note: candidate function template not viable: no known conversion
//       from 'const char [15]' to 'int' for 2nd argument

// It's trying to replace the default values:
auto err = makeError<FileError>(fileName: "foo.exe", lineNum: "Invalid header", funcName: __builtin_FUNCTION(), args...: );

// But I only want to supply the parameter pack args:
auto err = makeError<FileError>(fileName: __builtin_FILE(), lineNum: __builtin_LINE(), funcName: __builtin_FUNCTION(), args...: "foo.exe", "Invalid header");
Run Code Online (Sandbox Code Playgroud)

将参数包移动到参数列表的前面似乎也不能解决这个问题。有什么方法可以在保留可选参数的同时实现这种完美转发?

Godbolt 上的最小可重复示例

Yak*_*ont 5

template<class ErrorTy>
auto makeError(
    const char* fileName = __builtin_FILE(),
    int lineNum = __builtin_LINE(),
    const char* funcName = __builtin_FUNCTION()
) {
  return [=](auto&&... args) {
    return std::make_unique<ContextualError<ErrorTy>>(
      fileName, lineNum, funcName,
      decltype(args)(args)... // yes, this perfect fowards
    );
  };
}
Run Code Online (Sandbox Code Playgroud)

用途是:

auto err = makeError<FileError>()("foo.exe", "Invalid header");
Run Code Online (Sandbox Code Playgroud)

看到额外的()。可选参数进入(),转发的参数进入第二组()

基于您的现场示例

你也可以创建makeError一个class并给它一个operator()导致实际错误的,并为它的ctor提供默认参数。然后你会得到:

auto err = makeError<FileError>{}("foo.exe", "Invalid header");
Run Code Online (Sandbox Code Playgroud)

这可能不那么令人困惑,或者更令人困惑。其中之一。