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

Książka "The modern C++ Challenge"
CE
  • Rejestracja:około 4 lata
  • Ostatnio:około 3 lata
  • Postów:48
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.

Kopiuj
   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?
Kopiuj
   ipv4(ipv4 const & other) noexcept : data(other.data) {}
   ipv4& operator=(ipv4 const & other) noexcept 
   {
      data = other.data;
      return *this;
   }
edytowany 4x, ostatnio: Cepo
Spearhead
  • Rejestracja:prawie 6 lat
  • Ostatnio:około 3 godziny
  • Postów:1002
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).

edytowany 2x, ostatnio: Spearhead
AL
  • Rejestracja:prawie 11 lat
  • Ostatnio:około 3 lata
  • Postów:1493
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.
edytowany 2x, ostatnio: alagner
CE
Czy to nie byłoby lepsze? ipv4(ipv4 const & other) noexcept = default; i ipv4& operator=(ipv4 const & other) noexcept = default;
AL
Generalnie - byłoby. Ale nie jestem pewny (sprawdzę) czy jeżeli konstruktor data może rzucić to się to uda. ZTCW noexcept jest defaultowo dodawane do konstr. kopiujących i operatorów przypisania jeśli wewnątrz wołąją tylko takie fkcje.
AL
@Cepo: https://godbolt.org/z/zKKWEj6ch jak widać to działa. Ale czy powinno - nie jestem pewny, musiałbym spojrzeć do standardu.
TomaszLiMoon
Działa gdyż konstruktor kopiujący klasy C jest jawnie noexcept. Jeżeli chcesz sprawdzić czy złożony obiekt spełnia is_nothrow_copy_constructible : https://godbolt.org/z/TeKG4Yf5b
AL
@TomaszLiMoon: traita znam. Pytanie moje jest następujące: czy fakt, że pole klasy ma konstruktor kopiujący noexcept(false) pozwala legalnie zrobić w klasie zawierającej go (const T&) noxcept(true) = default (bo defaultowo on by jednak noexcept nie był. Tzn. na pewno się to kompiluje, pytanie czy nie trafiam tu w UB.
TomaszLiMoon
  • Rejestracja:prawie 10 lat
  • Ostatnio:dzień
  • Postów:530
3

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

Kopiuj
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.

AL
  • Rejestracja:prawie 11 lat
  • Ostatnio:około 3 lata
  • Postów:1493
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ć.

edytowany 1x, ostatnio: alagner
FS
nie da sie modyfikowac stałej, nawet przekazanej lokalnie przez wartość (chyba, że hakiem zdejmiesz lub const_cast)
AL
@FullSnack: wiem, pkt b dotyczył "przekazania przez non-const wartość" (jeśli nie było to wystarczająco jasne).

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.