Książka "The modern C++ Challenge"

0

Zainteresowała mnie książka "The modern C++ Challenge" i postanowiłem znaleźć jakieś przykłady czego mogę się po niej spodziewać. Włączyłem przykład 15 polegający na implementacji klasy reprezentującej adres ipv4. Kod autora znalazłem tutaj https://github.com/PacktPublishing/The-Modern-Cpp-Challenge/blob/master/Chapter02/problem_15/main.cpp

Moje wątpliwości wzbudziły dwie rzeczy:
1.

   constexpr ipv4(unsigned char const a, unsigned char const b, 
                  unsigned char const c, unsigned char const d): data{{a,b,c,d}} {}

Po co argumenty są oznaczone const?

  1. W jakim celu zostało zdefiniowane kopiowanie? Z tego co widzę implementacja jest taka sama jak generowana przez kompilator, a dodatkowo dochodzi do niejawnego usunięcia konstruktora i operatora przenoszącego, jeżeli taki był cel autora to czy nie powinno to być zrobione jawnie?
   ipv4(ipv4 const & other) noexcept : data(other.data) {}
   ipv4& operator=(ipv4 const & other) noexcept 
   {
      data = other.data;
      return *this;
   }
1
  1. Const correctness, generalnie dobrą praktyką jest oznaczanie jako stałego wszystkiego, czego się nie modyfikuje, w tym argumentów funkcji. Ponieważ często się tego nie pamięta robić, Rust specjalnie zrobił na odwrót i wszystko domyślnie jest stałe - dopiero trzeba jawnie oznaczyć jako mut żeby móc cokolwiek zmodyfikować.

  2. Nie wiem, może chodzi o to, że automatycznie wygenerowane nie będzie noexcept? (te wszystkie zasady są dość pogmatwane, a potencjalne mikroptymalizacje pewno nie warte zachodu, więc też bym raczej szedł w stronę rule of zero/rule of five).

1
  1. Jeżeli te chary idą przez wartość to const jest nadmiarowe. Generalnie const przy przekazywaniu przez wartość ma sens jeśli modyfikacja obiektu da jakieś skutki uboczne (na ile takie obiekty to dobry design - osobny temat).

Ew. może chodzić o jasne wyrażenie intencji. Ale to nadal imho nadmiarowe.

  1. W zależności jak wygląda konstruktor data może (lub nie) chodzić o noexcept. Ale jeśli nie - to autor mógł użyć default/delete.
3

Użycie **const **nie jest tutaj nadmiarowe, ale jest zabezpieczeniem przed przypadkową zmianą wartości.
Na przykład taki kod nie przejdzie.

constexpr ipv4(unsigned char const a, unsigned char const b, 
                  unsigned char const c, unsigned char const d):
      data{{++a,b,c,d}} {}

Tutaj oczywiście jest to trochę naciągane, gdyż od razu widać błąd. Jednakże w przypadku, kiedy konstruktor będzie składał się z wielu linii kodu, podobny przypadek może zostać nie zauważony.

0

@TomaszLiMoon: zgoda, "nadmiarowe" to jest może mało precyzyjne słowo, natomiast
a. "popsujesz" move semantics (co tu też jest bez znaczenia)
b. ew. modyfikacja nastąpi najwyżej lokalnie, bo przecież to kopia (ok, ok, span, i wszystkie podobne referencjo-podobne obiekty). Jeżeli piszesz mocno defensywnie - nie ma tu sporu, to może się przydać.
Ale z drugiej strony warto rozważyć czy faktycznie ma sens to robić.

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