Wywołanie konstruktora przenoszącego

Wywołanie konstruktora przenoszącego
PK
  • Rejestracja:prawie 3 lata
  • Ostatnio:około miesiąc
  • Postów:63
0

Witam !
Mam taki kod

Kopiuj
class Temp {
public:
  Temp(int a): a_{a} {std::cout << "Temp" << std::endl;}
  Temp(const Temp& t): a_{t.a_} {std::cout << "copy Temp" << std::endl;}
  Temp(Temp&& t): a_{t.a_} {std::cout << "move Temp" << std::endl;}
  auto show() {
    return a_;
  }
private:
 int a_{};
};

template <typename T>
void func(T t) {
  std::cout << t.show() << std::endl;
}

int main() {
  Temp t{100};
  func(t);
  func(std::move(Temp{3}));
}

output to:

Kopiuj
Temp
copy Temp
100
Temp
move Temp
3

Tak jak się spodziewałem. Tylko teraz jak zamienie typ argumentu w void func(T t) na void func(T&& t) to output mam

Kopiuj
Temp 
100
Temp
3

Dlaczego nie ma tutaj wywołania konstruktora przenoszącego move Temp ?

edytowany 2x, ostatnio: MarekR22
mwl4
  • Rejestracja:około 12 lat
  • Ostatnio:15 dni
  • Lokalizacja:Wrocław
  • Postów:399
3

Ponieważ T t jest obiektem. Żeby stworzyć obiekt musisz wywołać jakiś konstruktor. Stąd copy Temp i move Temp.
Za to T&& t to referencja - nowy obiekt nie jest tworzony, więc nie jest wołany konstruktor.


Asm/C/C++
PK
  • Rejestracja:prawie 3 lata
  • Ostatnio:około miesiąc
  • Postów:63
0

To T&& oznacza referencję do l-value i r-value ? Czym się rózni T&& od const T& ?

Jak zmieniłem typ argumentu na const T& to program trzeba skompilować z flagą -fpermissive bo wywala
error: passing ‘const Temp’ as ‘this’ argument discards qualifiers [-fpermissive] 18 | std::cout << t.show() << std::endl;

edytowany 2x, ostatnio: ProgrammingKing
mwl4
  • Rejestracja:około 12 lat
  • Ostatnio:15 dni
  • Lokalizacja:Wrocław
  • Postów:399
2

Jeśli chcesz użyć const T & to nie musisz kompilować z -fpermissive tylko możesz naprawić kod.
Problem jest tutaj:

Kopiuj
auto show() {

Metody show nie da się wywołać na stałym obiekcie (metoda nie jest const).

Jeśli zrobisz tak:

Kopiuj
auto show() const {

To kod się kompiluje.


Asm/C/C++
edytowany 1x, ostatnio: mwl4
PK
  • Rejestracja:prawie 3 lata
  • Ostatnio:około miesiąc
  • Postów:63
0

dziękuje za pomoc !

mwl4
  • Rejestracja:około 12 lat
  • Ostatnio:15 dni
  • Lokalizacja:Wrocław
  • Postów:399
1

To T&& oznacza referencję do l-value i r-value ? Czym się rózni T&& od const T& ?

T&& to r-value reference.

Różni to się tym, że jak masz:

Kopiuj
void func(T &&t) {

to t możesz przenieść do innego obiektu.

A jak masz:

Kopiuj
void func(const T &t) {

to nie możesz.

Dodatkowo, przy T &&t jak chcesz wywołać func to musisz dysponować obiektem który nie jest const.

Edit:
Chociaż, niezupełnie. Jakoś zignorowałem, że to jest szablon. template< typename T > w połączeniu z T && to perfect forwarding. Czyli taki typ jaki podasz przy wołaniu funkcji, zostanie dostarczony do funkcji.


Asm/C/C++
edytowany 2x, ostatnio: mwl4
PK
Masz u mnie 0.5 dobrej wódki. Wreszcie wiem czym się róznią te dwa zapisy.
mwl4
Chociaż to też jest ogólnie trochę bardziej zawiłe. Bo template< typename T > w połączeniu z T && to tzw. perfect forwarding.
PK
  • Rejestracja:prawie 3 lata
  • Ostatnio:około miesiąc
  • Postów:63
0

A co się dzieje jak mamy typ argumentu funkcji T&& a przekażemy do funkcji l-value? Wtedy funkcja to odczyta jak referencję do l-value (T& ) ?

mwl4
  • Rejestracja:około 12 lat
  • Ostatnio:15 dni
  • Lokalizacja:Wrocław
  • Postów:399
1
ProgrammingKing napisał(a):

A co się dzieje jak mamy typ argumentu funkcji T&& a przekażemy do funkcji l-value? Wtedy funkcja to odczyta jak referencję do l-value (T& ) ?

To będzie l-value.

Do zrozumienia perfect forwarding zrób sobie coś takiego:

Kopiuj
template <typename T>
void func(T &&t) {
  if( std::is_same_v< decltype( t ), Temp > ) std::cout << "type: Temp" << std::endl;
  if( std::is_same_v< decltype( t ), Temp & > ) std::cout << "type: Temp &" << std::endl;
  if( std::is_same_v< decltype( t ), const Temp & > ) std::cout << "type: const Temp &" << std::endl;
  if( std::is_same_v< decltype( t ), Temp && > ) std::cout << "type: Temp &&" << std::endl;
  if( std::is_same_v< decltype( t ), const Temp && > ) std::cout << "type: const Temp &&" << std::endl;
}

I wywołaj tak jak wołasz:

Kopiuj
int main() {
  Temp t{100};

  func(t);
  func(std::move(Temp{3}));

  const Temp &cr = t;
  func( cr );

  Temp &r = t;
  func( r );

  const Temp &&rr = std::move( t );
  func( std::move( rr ) );
}
Kopiuj
Temp
type: Temp &
Temp
type: Temp &&
type: const Temp &
type: Temp &
type: const Temp &&

https://godbolt.org/z/7Yqova1dh


Asm/C/C++
edytowany 6x, ostatnio: mwl4
PK
MarekR22
Moderator C/C++
  • Rejestracja:około 17 lat
  • Ostatnio:22 minuty
1

Naprawdę nikomu nie podnosi się alarm jak ktoś pisze std::move(Temp{3})?
Temp{3} to r-value więc std::move jeszcze pogarsza sprawę, co kompilator wyłapuje (jak korzysta się z ostrzeżeń): zmodyfikowana wersja:

Kopiuj
#include <iostream>
#include <utility>

#define LOG(X) std::cout << __PRETTY_FUNCTION__ << ' ' \
    << #X "=" << (X) << std::endl

class Temp {
public:
    Temp(int a)
        : a_ { a }
    {
        LOG(a);
    }
    Temp(const Temp& t)
        : a_ { t.a_ }
    {
        LOG(a_);
    }
    Temp(Temp&& t)
        : a_ { t.a_ }
    {
        LOG(a_);
    }
    auto show()
    {
        return a_;
    }

private:
    int a_ {};
};

template <typename T>
void func(T t)
{
    LOG(t.show());
}

int main()
{
    Temp t { 100 };
    func(t);
    func(std::move(Temp { 3 }));
}

Co produkuje ostrzeżenie:

Kopiuj
<source>: In function 'int main()':
<source>:43:19: warning: moving a temporary object prevents copy elision [-Wpessimizing-move]
   43 |     func(std::move(Temp { 3 }));
      |          ~~~~~~~~~^~~~~~~~~~~~
<source>:43:19: note: remove 'std::move' call

Bez move jest lepiej https://godbolt.org/z/MbzG3vYn6
Ale jeszcze lepiej skorzystać z perfect forwarding: https://godbolt.org/z/oW3Eh5dx1

Polecam doczytać jakaaś dobrą książkę o perfect forwarding (np coś od Mayersa). To jest dość zawiły temat, gdzie trudno coś dobrze wytłumaczyć w krótkiej wypowiedzi internetowej.


Jeśli chcesz pomocy, NIE pisz na priva, ale zadaj dobre pytanie na forum.
edytowany 3x, ostatnio: MarekR22
TR
  • Rejestracja:około 16 lat
  • Ostatnio:5 miesięcy
0
ProgrammingKing napisał(a):

To T&& oznacza referencję do l-value i r-value ? Czym się rózni T&& od const T& ?

Dokładna odpowiedź to: "to zależy". W przypadku tego template'a to będzie universal reference (zarówno l-value jak i r-value w zależności od tego, z jakim argumentem wywołasz funkcję).
Polecam lekturę tego artykułu: https://isocpp.org/blog/2012/11/universal-references-in-c11-scott-meyers

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.