具有移动语义的 C++ 可变参数模板函数

Per*_*tte 4 c++ variadic-templates

在 C++ 中,我有一组可变参数模板函数,我希望接受任意数量的参数,可以是常量引用,也可以作为右值引用(这样我可以在可能的情况下移动内容而不是复制)。但是,我发现即使我将参数包装在std::move().

// Accept end of parameters.
void example () {}

// Accept a non-constant R-value reference to do a move
template <typename... More>
void example (std::string &&value, More... parameters) {
    std::cout << value << ": moved" << std::endl;
    example (parameters...);
}

// Accept a constant reference parameter.    
template <typename... More>
void example (const std::string &value, More... parameters) {
    std::cout << value << ": copied" << std::endl;
    example (parameters...);
}

int main (int, char **) {
    std::string first { "first" };
    std::string second { "second" };
    std::string third { "third" };

    std::cout << "Trying variadic with move as second parameter: " << std::endl;
    example (first, std::move (second), third);
    // std::cout << "Trying variadic with move as first parameter: " << std::endl;
    // This next line won't even compile when uncommented
    // example (std::move (first), std::move (second), third);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

输出是:

Trying variadic with move as second parameter: 
first: copied
second: copied
third: copied
Run Code Online (Sandbox Code Playgroud)

而不是预期的:

Trying variadic with move as second parameter: 
first: copied
second: moved
third: copied
Run Code Online (Sandbox Code Playgroud)

作为奖励,当我将第一个参数包装在 中时std::move(),我在 g++7 和 clang 9 上都收到编译错误。

我究竟做错了什么?

Hol*_*Cat 5

这里有几个问题:

  • More... parameters始终按值接收参数(只要推导模板参数),因为typename ...More永远不会将类型推导为引用。

  • example传递给in 的所有参数example(parameters...);始终是左值。

  • 重载string &&无法调用该重载const string &,因为此时尚未声明它。

您应该使用转发引用和 ,而不是按值传递std::forward。并且您需要const string &在定义重载之前声明重载string &&

void example() {}

// Declare lvalue overload.
template <typename ...More>
void example(const std::string &value, More &&... parameters);

// Rvalue overload.
template <typename... More>
void example (std::string &&value, More &&... parameters) {
    std::cout << value << ": moved" << std::endl;
    example(std::forward<More>(parameters)...);
}

// Lvalue overload.
template <typename ...More>
void example(const std::string &value, More &&... parameters) {
    std::cout << value << ": copied" << std::endl;
    example(std::forward<More>(parameters)...);
}
Run Code Online (Sandbox Code Playgroud)

  • 请注意,要使其工作,您必须在定义第二个函数之前声明第二个函数,否则它将不知道在递归步骤中调用什么。 (3认同)