Search code examples
c++c++17stdmove-semanticsperfect-forwarding

To forward or move or not; how to determine which is preferred within the context of a class's usage?


class Test {
public:
    typedef std::set<std::pair<double, double>> DataSet;

    explicit Test(const DataSet&& d) {
        for (auto &itr : d) {
            std::cout << "value1 = " << itr.first << '\n';
            std::cout << "value2 = " << itr.second << '\n';
        }
    }

};

int main() {
    //using namespace util;
    try {
        // Forwarding
        Test obj( std::forward<Test::DataSet>(
            { 
              { 10.0, 20.0 },
              { 30.0, 40.0 },
              { 50.0, 60.0 }
            }
        ));

        std::cout << '\n';

        // Move
        Test obj2(std::move(Test::DataSet(
            {
              { 10.0, 20.0 },
              { 30.0, 40.0 },
              { 50.0, 60.0 }
            }
           )
        ));

    } catch (const std::exception& e) {
        std::cerr << e.what() << std::endl;
        return EXIT_FAILURE;
    }

    return EXIT_SUCCESS;
}

Solution

  • Neither.

    Test::DataSet(/*...*/) is already a rvalue that can bind to a rvalue reference. The only thing std::move does is to turn a lvalue into a xvalue (which is a rvalue), so that it can bind to a rvalue reference, which lvalues cannot. So std::move is redundant.

    std::forward does the same only conditional on its template argument. So it is redundant (at best) as well.

    const DataSet&& as parameter makes no sense. You want either DataSet&& which binds only to rvalues or const DataSet&, which binds to rvalues and lvalues.

    If you want to make use of the fact that the passed reference might be a rvalue (i.e. by moving from its members), then you need two overloads of your function. One taking DataSet&& which is called with rvalue arguments and one taking const DataSet& which is called with lvalue arguments.

    In the specific case of the Test function in your question, I don't see how there would be any benefit to moving from the members or do any other modification of the DataSet state, so I would go with simply const DataSet& as parameter and never bother with std::moveing or std::forwarding into that function specifically.