std::move w konstruktorze przenoszącym

0

Cześć.

Mam pytanie dlaczego jak mam konstruktor przenoszący:

SimpleStringOwner(SimpleString&& x) : string{ std::move(x) } {}

To trzeba użyć string { std::move(x) } a nie samo string {x} ?
Kompilator sam nie wydedukuje z kodu że x jest rvalue skoro ma typ SimpleString&& ?

5

x to jest l-value.
std::move fizycznie nic nie robi, jedynie konwertuje l-value do r-value, dzięki czemu wybrany zostanie właściwy konstruktor dla pola string (domyślam się, że to pole jest typu SimpleString i ten typ ma move-constructor).

Radzę podać kompletny minimalny reprodukowany przykład a nie malutkie fragmenty, które wymagają domyślania się kontekstu.

0

Dzięki za pomoc.

2

obejrzyj sobie 50 shades of cpp

nikolai właśnie robi taką akcję z std::move w konstruktorze i omawia to co cię interesuje.

1
SimpleStringOwner(SimpleString&& x)
  : string{ x }
{
  // std::move() nie zostało użyte na x, więc mogę tutaj zakładać, że obiekt nie został "przeniesiony"
}
SimpleStringOwner(SimpleString&& x)
  : string{ std::move(x) }
{
  // std::move() zostało użyte na x, więc zakładam, że obiekt został już "przeniesiony". Nie mogę więc nic zrobić z x poza destrukcją.
}

Dla kontrastu:

SimpleStringOwner(SimpleString&& x)
  : stringCopy{ x }
{
  string = std::move( x );
}
2
Chce_byc_programista napisał(a):

To trzeba użyć string { std::move(x) } a nie samo string {x} ?
Kompilator sam nie wydedukuje z kodu że x jest rvalue skoro ma typ SimpleString&& ?

No właśnie nie wydedukuje, bo ma zakazane – jakoś na krótko przed zatwierdzeniem C++11 z move semantics, zdecydowano że coś co ma nazwę (tu: x) nie jest nigdy traktowane jako T&& nawet jeśli formalnie ma taki typ. Czyli string {x} nie może użyć konstruktora przenoszącego jeśli x jest nazwą zmiennej o jakimkolwiek typie.
Żeby dostać prawdziwe T&& to musi być to wynik jakiegoś wyrażenia, np. rzutowania albo być wartością zwracaną z funkcji. move<T>() zwraca podany argument rzutując każde T na odpowiadający mu T&&; równie dobrze można by było napisać string { (SimpleString&&)x } albo podobnie.

0

Jeszcze mam jedno pytanie, mam klasę:

struct SimpleString {
    explicit SimpleString(size_t max_size): max_size{ max_size }, length{} {
        if (max_size == 0) {
            throw std::runtime_error{ "Max size must be at least 1. "};
        }
        printf("MAX size %d\n", max_size);
        buffer = new char[max_size];
        buffer[0] = 0;
        printf("Constructor 1 \n");
    }
    ~SimpleString(){
        printf("Destructor 1\n");
        delete[] buffer;
    }
    SimpleString(SimpleString &other) = delete;
    SimpleString(SimpleString &&other) = delete;
 private:
    size_t max_size;
    char *buffer;
    size_t length;
};

Jak to jest że wywołanie
SimpleString zm{SimpleString(10)}; wywołuje konstruktor? Jak w kodzie klasy konstruktor przyjmuje tylko jedną wartość size_t max_size.

1
Chce_byc_programista napisał(a):

Jak to jest że wywołanie
SimpleString zm{SimpleString(10)}; wywołuje konstruktor?

Czy na pewno skompilowałeś z tym wywolaniem?
Bo kompilator powinien się pluć, np:
[Error] use of deleted function 'SimpleString::SimpleString(SimpleString&&)'

1

dlaczego takie coś

SimpleString zm{SimpleString(10)};

przechodzi na 17 a na innych nie. Zdaje mi się przez zmiany w 17. Otóż to będzie coś w rodzaju(i to w bardzo dużym uogólnieniu!)

SimpleString zm{10};

można by poczytać tu
https://en.cppreference.com/w/cpp/language/copy_elision
cytując nikolaia "tortura"

1 użytkowników online, w tym zalogowanych: 0, gości: 1