4
votes

For the program code below, I have to write the the same code inside the pair of member functions which receiving rvalue and lvalue references.

My aim is to use only one from the pair (e.g.; only use the rvalue accepting ones), and the others. I read the reference of std::forward, as far as I understood, it looks like it is for this purpose. But, when I delete the lvalue reference taking ones, I get the following compiler error.

'TestClass::TestClass(const TestClass &)': cannot convert argument 1 from 'std::wstring' to 'std::wstring &&'

How do I prevent this code duplication?

#include <iostream>
#include <string>

class TestClass
{
    public:
        TestClass(const std::wstring    &  Text)
            : Text(Text)
        {
            std::wcout << L"LValue Constructor : " << Text << std::endl;
            /*Some code here...*/
        }
        TestClass(      std::wstring    && Text)
            : Text(std::forward<std::wstring>(Text))
        {
            std::wcout << L"RValue Constructor : " << this->Text << std::endl;
            /*Same code here...*/
        }
        TestClass(const TestClass       &  Another)
            : Text(Another.Text)
        {
            std::wcout << L"Copy   Constructor : " << Text << std::endl;
            /*Some code here...*/
        }
        TestClass(      TestClass       && Another)
            : Text(std::forward<std::wstring>(Another.Text))
        {
            std::wcout << L"Move   Constructor : " << Text << std::endl;
            /*Same code here...*/
        }

    private:
        std::wstring Text;
};

int wmain(int argc, wchar_t *argv[], wchar_t *envp[])
{
    std::wstring Argument(L"Testing Copy");
    TestClass Class1Copy(Argument);
    TestClass Class1Move(L"Testing Move");
    TestClass Class2Copy(Class1Copy);
    TestClass Class2Move(std::move(Class1Move));

    _wsystem(L"pause");
    return 0;
}

Output:

LValue Constructor : Testing Copy  
RValue Constructor : Testing Move  
Copy   Constructor : Testing Copy  
Move   Constructor : Testing Move  
Press any key to continue . . .
3
Text(std::forward<std::wstring>(Text)) should really be Text(std::move(Text)). Same in the other places you have used forward instead of move. - Simple

3 Answers

2
votes

You can take by value and then move. Then you only need N overloads, not 2N:

TestClass(std::wstring Text)
    : Text(std::move(Text))
{
}

You can avoid the copy constructor and move constructor duplication by writing nothing at all; the compiler will generate them by default in this case.

2
votes

If move construction is expected to be extremely cheap, you can take by value and move-from the value. This does exactly 1 more move than a pair of copy and move overloads.

If you want optimal efficiency, and/or if move construction is cheaper but not cheap enough to neglect, you can forward:

template<class T>
std::decay_t<T> copy(T&& t) {
  return std::forward<T>(t);
}
class TestClass {
public:
  TestClass(std::wstring const&  Text)
    TestClass( copy(Text) )
  {}
  TestClass(TestClass const& o)
    : TestClass( o.Text )
  {}
  TestClass(TestClass&& o)
    : TestClass( std::move(o).Text ) // pattern does the right thing more often than `std::move(o.Text)` does.
  {}
  // only "real" ctor:
  TestClass( std::wstring&& Text)
    : Text(std::forward<std::wstring>(Text))
  {
    std::wcout << L"RValue Constructor : " << this->Text << std::endl;
    /*Code here...*/
  }
// ...

now everything forwards down to the one constructor.

You can even mix the two techniques: use by-value for std::wstring (as we know that is cheap-to-move) and do forwarding stuff for the TestClass code (or anything less likely to be stable).

0
votes

I don't think you can do that since the function's signatures determine when and where they are used. It's like a copy constructor and an assignment operator. They do somewhat similar things but the compiler calls the appropriate one basee on context.

If you want to avoid reusing code, just factor out the commonality into a separate function.