Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ variadic template function with move semantics

In C++, I have a set of variadic template functions that I'd like to accept an arbitrary number of parameters, either constant references or as r-value references (so I can move things rather than copy when possible). However, I find that the version that accepts the constant reference is called even when I'm wrapping arguments in 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;
}

The output is:

Trying variadic with move as second parameter: 
first: copied
second: copied
third: copied

instead of the expected:

Trying variadic with move as second parameter: 
first: copied
second: moved
third: copied

And as a bonus, when I wrap the first argument in std::move(), I get a compile error on both g++7 and clang 9.

What am I doing wrong?

like image 575
Perette Avatar asked Nov 16 '25 04:11

Perette


1 Answers

There are several problems here:

  • More... parameters always receives arguments by value (as long as the template parameters are deduced), because types in typename ...More will never be deduced as references.

  • All arguments passed to example in example(parameters...); will always be lvalues.

  • The string && overload can't call the const string & one, because it's not yet declared at that point.

Instead of passing by value, you should use forwarding references and std::forward. And you need to declare the const string & overload before defining the string && one.

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)...);
}
like image 95
HolyBlackCat Avatar answered Nov 17 '25 18:11

HolyBlackCat



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!