Konstruktor przenoszący

0

Witam!

Jak napisać w C++ (nie w c++11) konstruktor przenoszący?
Jestem programistą C, kiedyś pisałem coś w C++, ale swoją wiedzę oceniam jako dno, chcę powtórzyć i zrozumieć wszystkie podstawowe mechanizmy.

Doczytałem, że każda klasa ma domyślne konstruktory i operatory, które nie są jawne. Chciałbym nadpisać je wszystkie, by zrozumieć jak działają konstruktory/operatory kopiowania i przenoszenia.

#include <iostream>

class Foo{
public:
    Foo(int foo)
    {
        // std::cout << __FUNCTON__ << std::endl; // wyswietla nazwe klasy :(
        std::cout << "Foo(int foo)" << std::endl;
    }

    Foo(Foo& other)
    {
        std::cout << "Foo(Foo& other)" << std::endl;
    }

    Foo(const Foo& other)
    {
        std::cout << "Foo(const Foo& other)" << std::endl;
    }

    Foo& operator=(Foo& other)
    {
        std::cout << "Foo& operator=(Foo& other)" << std::endl;
        return *this;
    }

    Foo& operator=(const Foo& other)
    {
        std::cout << "Foo& operator=(const Foo &other)" << std::endl;
        return *this;
    }

    void print() const
    {
        std::cout << "void test() const" << std::endl;
    }
private:

};

Foo test(){
    Foo foo(2);

    return foo;
}

int main(){
    Foo foo = test();
    foo.print();
    return 0;
}

Wykonując return foo, spodziewałem się że wykona się chociaż konstruktor/operator kopiowania, lecz wynik jest taki:

Foo(int foo)
void test() const
0

http://en.cppreference.com/w/cpp/language/rule_of_three

http://stackoverflow.com/questions/4782757/rule-of-three-becomes-rule-of-five-with-c11

Zwracasz lokalna zmienna ktora po wykoaniu funkcji juz nie istnieje. Jakbys zdefiniowal destruktor to bys zobaczyl ;). to co robisz to jest UB

0
fasadin napisał(a):

http://en.cppreference.com/w/cpp/language/rule_of_three

http://stackoverflow.com/questions/4782757/rule-of-three-becomes-rule-of-five-with-c11

Tak właśnie, dzisiaj na forum to znalazłem i postanowiłem wziąć się za siebie, lecz tutaj jest konstruktor przenoszący, lecz w c++11:

    rule_of_five(rule_of_five&& other) : cstring(other.cstring) // move constructor
    {
        other.cstring = nullptr;
    }

A kod który napisałem, działa inaczej jak podejrzewałem...
Nie wykonał się nawet konstruktor kopiujący.

1

Zadziałała optymalizacja return value optimization. Jeśli kompilujesz ten kod przy użyciu gcc/clanga możesz wyłączyć tę optymalizacje za pomocą -fno-elide-constructors.

0
satirev napisał(a):

Zadziałała optymalizacja return value optimization. Jeśli kompilujesz ten kod przy użyciu gcc/clanga możesz wyłączyć tę optymalizacje za pomocą -fno-elide-constructors.

Ooo i przez to mi tutaj coś nie pasowało.
Czyli jeśli taka optymalizacja jest wyłączona dla c++, to taki zapis

Foo test(){
    Foo foo(2);
 
    return foo;
}

nie ma sensu, jeśli klasa foo przechowuje ogromne ilości danych, bo musi zaalokować dane, przekopiować i stare wywalić.
A w c++11, dzięki konstruktorom przenoszącym jest ok, tak?

0
satirev napisał(a):

Nie. Poczytaj jak to działa http://en.cppreference.com/w/cpp/language/copy_elision

Ale nie? czy nie tego co napisałem że nie?

Widzę że to co kryje się pod linkiem to dotyczy optymalizacji, a mi chodzi o działanie gdy jest podany argument -fno-elide-constructors.
Czyli w c++ nie ma konstruktora/operatora przenoszącego i przy wyłączonej optymalizacji zostaje kopiowanie?
Taki mam wynik przy -fno-elide-constructors

Foo(int foo)
Foo(Foo& other)
Foo(const Foo& other)
void test() const
0

Jak napisać w C++ (nie w c++11) konstruktor przenoszący?

Nie wiem „jak napisać”, ale biblioteka Boost::Move potrafi symulować przenoszenie na kompilatorach nie obsługujących C++11.

#include <boost/move/move.hpp>

class Foo
{
  BOOST_COPYABLE_AND_MOVABLE(Foo)

public:
  // konstruktor kopiujący
  Foo::Foo(const Foo& other)
  {
  }

  // kopiujący operator przypisania
  Foo& Foo::operator =(BOOST_COPY_ASSIGN_REF(Foo) other)
  {
      return *this;
  }

  // konstruktor przesuwający
  Foo::Foo(BOOST_RV_REF(Foo) other)
  {
  }

  // przesuwający operator przypisania
  Foo& Foo::operator =(BOOST_RV_REF(Foo) other)
  {
      return *this;
  }
};

Do przenoszenia takiego obiektu używamy funkcji boost::move.

Jeżeli nie zrobiłem literówki to powyższy kod będzie działał np. na Visual C++ 2008, który nie miał jeszcze referencji do r-wartości.

PS. jest też BOOST_MOVABLE_BUT_NOT_COPYABLE, które moim zdaniem jest bardziej przydatne. Taka klasa będzie przesuwalna, ale nie kopiowalna.

2

W obecnym C++ (nie C++11) jest to dość proste:

struct Foo
{
	Foo(Foo&&) = default;
};

jeśli chcesz/musisz bardziej się męczyć możesz podać ciało funkcji.

0

Dziękuję za odpowiedzi!
Do niedawna myślałem że jest jeden C++, poznałem C++11 (o C++14 dopiero słyszę) i zaintrygowało mnie &&, więc zaciekawiło mnie jak to było robione wcześniej, domyślałem się że jest to związane z optymalizacją, lecz to wszystko jest "niewidoczne" pisząc kod, stąd te pytania.
Ucząc się czegokolwiek, mocno dociekam jakie były początki.
Teraz czas na analizę biblioteki boost ;)

0

w c++17 nadchodzi wiele dodatkowych zmian

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