Как предотвратить повторение кода между функциями-членами rvalue и lvalue?

Для приведенного ниже программного кода мне нужно написать тот же код внутри пары функций-членов, которые получают ссылки на rvalue и lvalue.

Моя цель - использовать только один из пары (например, использовать только те, которые принимают rvalue), а остальные. Прочитал ссылку std::forward, насколько я понял, похоже она для этого и предназначена. Но когда я удаляю ссылки на lvalue, я получаю следующую ошибку компилятора.

'TestClass::TestClass(const TestClass &)': невозможно преобразовать аргумент 1 из 'std::wstring' в 'std::wstring &&'

Как предотвратить дублирование кода?

#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;
}

Выход:

LValue Constructor : Testing Copy  
RValue Constructor : Testing Move  
Copy   Constructor : Testing Copy  
Move   Constructor : Testing Move  
Press any key to continue . . .

person hkBattousai    schedule 08.12.2015    source источник
comment
Text(std::forward<std::wstring>(Text)) на самом деле должно быть Text(std::move(Text)). То же самое и в других местах, где вы использовали forward вместо move.   -  person Simple    schedule 08.12.2015


Ответы (3)


Если ожидается, что строительство перемещения будет чрезвычайно дешевым, вы можете брать по стоимости и перемещать по стоимости. Это делает ровно на 1 перемещение больше, чем пара копий и перегрузок перемещения.

Если вам нужна оптимальная эффективность и/или если конструкция перемещения дешевле, но не настолько дешева, чтобы ею можно было пренебречь, вы можете переслать:

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...*/
  }
// ...

теперь все перенаправляется к одному конструктору.

Вы даже можете смешивать эти два метода: использовать по значению для std::wstring (так как мы знаем, что это дешево для перемещения) и выполнять пересылку для кода TestClass (или что-то менее стабильное).

person Yakk - Adam Nevraumont    schedule 08.12.2015

Вы можете взять по значению, а затем move. Тогда вам нужны только N перегрузки, а не 2N:

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

Вы можете избежать дублирования конструктора копирования и перемещения конструктора, вообще ничего не написав; в этом случае компилятор сгенерирует их по умолчанию.

person Simple    schedule 08.12.2015

Я не думаю, что вы можете это сделать, поскольку сигнатуры функций определяют, когда и где они используются. Это как copy constructor и assignment operator. Они делают несколько похожие вещи, но компилятор вызывает соответствующий в зависимости от контекста.

Если вы хотите избежать повторного использования кода, просто вынесите общие черты в отдельную функцию.

person Anon Mail    schedule 08.12.2015