Autor Wątek: Zagadki językoznawcze  (Przeczytany 60631 razy)

Offline Xirdus

  • Redaktor

# Czerwiec 03, 2014, 23:33:29
Zgadza się, Dab. Warto jednak zaznaczyć, że nie jest to cecha list inicjalizacyjnych, tylko inicjalizacji w ogóle - także przed C++11. Mianowicie jeśli klasa (ani żadna składowa, rekursywnie) nie ma zdefiniowanego przez użytkownika konstruktora, i inicjalizujemy ją pustymi nawiasami (a nie np. niczym), to nie zostanie wywołany domyślny konstruktor, tylko wypełnia się pamięć zerami. Użyłem listy inicjalizacyjnej zamiast nawiasów okrągłych tylko po to, żeby uniknąć Most Vexing Parse.

Offline Mr. Spam

  • Miłośnik przetworów mięsnych

Offline Xion

  • Moderator
    • xion.log

# Czerwiec 04, 2014, 00:19:17
Swego czasu używałem:
int a[128] = {0};do inicjowania tablic zerami. Przestałem to robić, bo -- zgodnie z tym co wyjaśniono wyżej -- rzeczony trik działa zgodnie z oczekiwaniami, jest tak z zupełnie innych powodów niż wiele osób by się spodziewało. (W szczególności zamiana 0 na inną wartość nie spowoduje wypełnienia tablicy tą wartością).

Offline Xender

  • Użytkownik

# Czerwiec 06, 2014, 14:54:39
Mianowicie jeśli klasa (ani żadna składowa, rekursywnie) nie ma zdefiniowanego przez użytkownika konstruktora, i inicjalizujemy ją pustymi nawiasami (a nie np. niczym), to nie zostanie wywołany domyślny konstruktor, tylko wypełnia się pamięć zerami. Użyłem listy inicjalizacyjnej zamiast nawiasów okrągłych tylko po to, żeby uniknąć Most Vexing Parse.
Oj, wypełnianie pamięci zerami AFAIK dawno wyleciało ze standardu na rzecz "inicjalizacji domyślnej" - co oznacza wpisanie 0 dla składowych typów podstawowych i wywołanie ctorów domyślnych dla składowych obiektów.
Jest tak dlatego, żebyś w żadnym wypadku po zainicjalizowaniu obiektu nie został z jego składowymi wypełnionymi binarnymi zerami zamiast poprawnie zainicjalizowanymi.

@Xion - Oczywiście. Pierwszy element zostanie ustawiony na podaną explicite wartość (tutaj akurat 0) lub tyle wartości, ile zostanie podane, a pozostałe elementy tablicy - dla których brakuje wartości w initliście - implicite zainicjalizowane domyślnie. Optymalnie byłoby po prostu móc napisać int a[128] = {};- ta składnia chyba nawet mi gdzieś działała, ale AFAIK nie jest standardowa (ani nawet zbyt przenośna) i może powodować błąd kompilacji.

Dodam, że "domyślna inicjalizacja" intopodobnych dotyczy tylko inicjalizowania w strukturze, tablicy itp. Nie dotyczy oczywiście intów na stosie.

Offline Xirdus

  • Redaktor

  • +1
# Czerwiec 06, 2014, 15:22:44
Oj, wypełnianie pamięci zerami AFAIK dawno wyleciało ze standardu na rzecz "inicjalizacji domyślnej" - co oznacza wpisanie 0 dla składowych typów podstawowych i wywołanie ctorów domyślnych dla składowych obiektów.
Jestes pewien? Bo ja wszedzie widze ze zerowa inicjalizacja dalej funkcjonuje i ma sie dobrze.

Jest tak dlatego, żebyś w żadnym wypadku po zainicjalizowaniu obiektu nie został z jego składowymi wypełnionymi binarnymi zerami zamiast poprawnie zainicjalizowanymi.
Kiedy nie ma konstruktora, binarne zera sa chyba najsensowniejsza inicjalizacja - chyba ze nie chce sie zadnej inicjalizacji w ogole.

@Xion - Oczywiście. Pierwszy element zostanie ustawiony na podaną explicite wartość (tutaj akurat 0) lub tyle wartości, ile zostanie podane, a pozostałe elementy tablicy - dla których brakuje wartości w initliście - implicite zainicjalizowane domyślnie. Optymalnie byłoby po prostu móc napisać int a[128] = {};- ta składnia chyba nawet mi gdzieś działała, ale AFAIK nie jest standardowa (ani nawet zbyt przenośna) i może powodować błąd kompilacji.
Pozwol mi zacytowac cppreference.com:
Cytuj
The effects of aggregate initialization are:
(...)
  • If the number of initializer clauses is less than the number of members or initializer clauses is completely empty, the remaining members are initialized (...) by empty lists, which performs value-initialization.
Cytuj
The effects of value initialization are:
(...)
  • If T is an non-union class type without any user-provided constructors, then the object is zero-initialized and then the implicitly-declared default constructor is called (unless it's trivial)

Offline Xender

  • Użytkownik

# Czerwiec 06, 2014, 18:29:39
^ O, "Value Initialization", nie "Default Initialization". Tak, o to mi chodziło. Chociaż "Default Initialization" też się przewija w kontekście, w sformułowaniu z C++14.

No więc, jak masz klasę/strukturę Foo, która zawiera kilka intów, i w której nie masz explicite zdefiniowanego ctora, to wypełnienie pamięci zerami ma sens.
Ale jeśli Foo zawiera obiekt klasy Bar, która to klasa Bar ma konstruktor (przyjmijmy że Bar to np. std::vector), to czy jedynie wypełnienie zerami całego obszaru pamięci właśnie inicjalizowanego ma sens? Nie, trzeba jeszcze wywołać ctory obiektów składowych.

No więc to co mówisz się zgadza, ale na tym rzecz się nie kończy - na czerwono jest ciąg dalszy:
1) If T is a class type with at least one user-provided constructor of any kind, the default constructor is called.
   (until C++14)
1) If T is a class type with no default constructor or with a user-provided default constructor or with a deleted default constructor, the object is default-initialized.
   (since C++14)

2) If T is an non-union class type without any user-provided constructors, then every non-static data member and base-class component of T is value-initialized
   (until C++11)
2) If T is an non-union class type without any user-provided constructors, then the object is zero-initialized and then the implicitly-declared default constructor is called (unless it's trivial)
   (since C++11)
   (until C++14)
2) If T is a class type without a user-provided or deleted default constructor (that is, it may be a class with a defaulted default constructor or with an implicitly-defined one) then the object is zero-initialized and then it is default-initialized if it has a non-trivial default constructor
   (since C++14)

3) If T is an array type, each element of the array is value-initialized

4) Otherwise, the object is zero-initialized.
Więc jak widać ze standardu na standard zmienia się sformułowanie, ale efekt jest taki sam - nie zostaniemy z nietrywialnymi membarami wypełnionymi zerami zamiast wywołania ich konstruktora.

IIRC w jakiejś naprawdę starej wersji standardu domyślny ctor kopiujący działał bajt-po-bajcie zamiast składowa-po-składowej. Jest to tego samego typu przeoczenie co inicjalizacja wszystkich memberów samymi zerami. Ale we współczesnych standardach (C++98) żadnego z tych zachowań AFAIK nie ma.

Offline Xirdus

  • Redaktor

# Czerwiec 06, 2014, 18:42:44
Ale jeśli Foo zawiera obiekt klasy Bar, która to klasa Bar ma konstruktor (przyjmijmy że Bar to np. std::vector), to czy jedynie wypełnienie zerami całego obszaru pamięci właśnie inicjalizowanego ma sens? Nie, trzeba jeszcze wywołać ctory obiektów składowych.
Tu się zgadzam - nigdy nie twierdziłem inaczej. Wyraźnie zaznaczyłem, że żeby zerowa inicjalizacja miała miejsce, ani klasa tworzonego obiektu, ani żadna z klas obiektów składowych nie może mieć konstruktora.

No więc to co mówisz się zgadza, ale na tym rzecz się nie kończy - na czerwono jest ciąg dalszy:
2) If T is an non-union class type without any user-provided constructors, then the object is zero-initialized and then the implicitly-declared default constructor is called (unless it's trivial)
:)

Offline Xender

  • Użytkownik

# Czerwiec 06, 2014, 18:52:06
^ Trywialny konstruktor to taki, który nic nie robi, więc welp... Czysta optymalizacja, tak jak inlining.

Offline Reg

  • Administrator
    • Adam Sawicki - Home Page

  • +1
# Lipiec 10, 2014, 22:24:56
Dyskusja na temat Unicode została wydzielona do wątku:
http://forum.warsztat.gd/index.php?topic=29054.0

Offline ΨΧΞ

  • Użytkownik
    • PsichiX Website

# Sierpień 05, 2014, 15:51:16
bool PtakopyskInterface::canIterateGameObjectsNext( bool isPrefab )
{
    if( !m_gameManager )
    {
        m_errors << "Game manager is null!\n";
        return false;
    }

    if( !m_gameObjectIsIterating )
        return false;
    if( m_gameObjectIteratorStack.empty() )
    {
        if( m_gameObjectCurrentIterator != m_gameManager->gameObjectAtEnd( isPrefab ) )
            return true;
    }
    else
    {
        GameObject::List::iterator p = m_gameObjectIteratorStack.top();
        GameObject* pgo = *p;
        if( pgo && m_gameObjectCurrentIterator != pgo->gameObjectAtEnd() )
            return true;
    }
    // m_errors << "iterator is not valid\n";
    return false;
}
Disclaimer: testowalem tylko na GCC.
Zagadka: jaki rezultat zwroci metoda z zakomentowana linijka oraz odkomentowana linijka, gdy skompilujemy kod w trybie Release (czyli z flaga optymalizacji), jesli wiemy, ze:
m_gameObjectIsIterating == true;
m_gameObjectIteratorStack.empty() == true;
m_gameObjectCurrentIterator == m_gameManager->gameObjectAtEnd( isPrefab );
:>

EDIT: blad jednak z mojej strony - nie marshalowalem zwracanego boola na UnmanagedType.U1 po stronie c#, przez co dostawal tego boola jako bajt false i kolejne bajty jako smiecie, co rozumial c# jako true :>
« Ostatnia zmiana: Sierpień 05, 2014, 16:18:57 wysłana przez ΨΧΞ »

Offline Xirdus

  • Redaktor

# Sierpień 05, 2014, 16:40:04
@ΨΧΞ, zasady konkursu mówią:
Cytuj
+ Nie stosujemy kompilatorów do odpowiedzi (!) Odpowiadajmy na podstawie wiedzy a nie, "wklepałem do IDE i zadziałało tak, więc musi być tak". Zawsze trzeba uargumentować odpowiedź: dlaczego dzieje się tak, a nie inaczej.
 + W sytuacjach spornych decydujący głos ma standard języka. Nawet jeżeli jakiś kompilator go nie przestrzega.
Ja je interpretuję tak, że "zagadki powinny dotyczyć specyfikacji języka, a nie konkretnych implementacji", tzn. pytania o wynik undefined behavior, implementation-defined behavior, czy jawne odstępstwa od standardu np. w celu optymalizacji się nie kwalifikują.

Offline ΨΧΞ

  • Użytkownik
    • PsichiX Website

# Sierpień 05, 2014, 16:51:12
aaah, w takim razie pardon :)

Offline Xion

  • Moderator
    • xion.log

# Sierpień 09, 2014, 19:51:08
Dyskusja na temat UB i goto wydzielona do osobnego wątku:

http://forum.warsztat.gd/index.php?topic=29159.0

Offline Xirdus

  • Redaktor

# Wrzesień 22, 2014, 23:46:31
Zagadka: do czego służy poniższe makro?
#define MY_MACRO(name, x) typedef int name[(x) * 2 - 1]
(Nazwa rzecz jasna zmieniona.)

Hint: x jest wyrażeniem.

Offline lethern

  • Użytkownik

# Wrzesień 23, 2014, 00:29:18
Pewnie coś analogicznego jak to:
typedef int ERROR_CantUsehorrible_cast[sizeof(function_to_bind)==sizeof(u.s)? 1 : -1];