LINUX.ORG.RU

Как объяснить GCC, что switch полный?

 , ,


0

4

Имеем:

enum class MyEnum
{
   One,
   Two,
}

QString myfunc(const MyEnum e)
{
    switch (e) {
        case MyEnum:One : return "One";
        case MyEnum:Two : return "Two";
    }
}
* код не запускал

GCC 5.4.0 с радостью вещает мне, что:

warning: control reaches end of non-void function [-Wreturn-type]

При том, что clang обрабатывает верно.

Как убрать этот варнинг без глобального флага и без #pragma GCC diagnostic ignored "-Wreturn-type"?

★★★★★

Последнее исправление: RazrFalcon (всего исправлений: 2)
Ответ на: комментарий от anonymous

Для экспертов, не отличающих gcc от libstdc++, объясняю: доступ к полям разрушенного объекта — UB, поэтому там вовсе не обязан оказаться null, даже если ты его туда записал в деструкторе.

Softwayer ★★
()
Ответ на: комментарий от i-rinat

Почему не использовать при отладке address sanitizer, который не только с тем же успехом уронит программу, но и нарисует красивые стектрейсы, показывающие, кто объект разрушил и кто его потом использовал?

Softwayer ★★
()
Ответ на: комментарий от Softwayer

Для экспертов, не отличающих gcc от libstdc++

Лол :-) Важно не это, важно то, что в деструкторе ~unique_ptr в GCC имеется присваивание impl_ = nullptr :-)

доступ к полям разрушенного объекта — UB, поэтому там вовсе не обязан оказаться null, даже если ты его туда записал в деструкторе.

Для экспертов, отличающих GCC от libstdc++, объясняю:

#include <memory>

int
main()
{
  std::unique_ptr<int> ptr;
  ptr.~unique_ptr();   // сегфолта не будет, спасибо GCC

  struct Ptr {
    ~Ptr()
    {
      delete impl_;
      impl_ = nullptr; // без этого будет сегфолт при выходе из main()
    }
    int* impl_;
  } ptr2 {new int()};
  ptr2.~Ptr();
}
Лол :-)

anonymous
()
Ответ на: комментарий от anonymous

Посоветовавшись с более опытным коллегой, как советовал Страуструп, он одобрил явный вызов деструктора :-) А ты почему не одобряешь? :-)

anonymous
()
Ответ на: комментарий от anonymous

Если ты всерьёз считаешь отсутствие сегфолта при вызове деструктора на уже разрушенном объекте благом, то вряд ли у меня получится тебя в чём-нибудь убедить.

Softwayer ★★
()
Ответ на: комментарий от Softwayer

Если ты всерьёз считаешь отсутствие сегфолта при вызове деструктора на уже разрушенном объекте благом, то вряд ли у меня получится тебя в чём-нибудь убедить.

Не мог бы ты пояснить, что такое в данном случае «уже разрушенный объект»? :-) И что меняет явный вызов деструктора? :-) Он что, отменяет второй автоматический вызов деструктора, где будет выполнен delete impl_? :-)

anonymous
()

На самом деле, забавно, что i-rinat и смайлодаун одним и тем же трюком пытаются достичь прямо противоположного: первый — словить сегфолт, а второй — избежать его.

Softwayer ★★
()

хочешь как в русте, пиши на русте, а в С/C++ по стандарту там должен быть ричибл код, не нравится ? не пиши на C/C++ и не создавай больше тупые темы

anonymous
()
Ответ на: комментарий от Softwayer

Это из разряда советов, которые либо очевидны, либо совершенно непонятны.

i-rinat ★★★★★
()
Последнее исправление: i-rinat (всего исправлений: 1)
Ответ на: комментарий от anonymous

Пожалуйста:

The lifetime of an object o of type T ends when:
(1.3) — if T is a class type with a non-trivial destructor (15.4), the destructor call starts, or
(1.4) — the storage which the object occupies is released, or is reused by an object that is not nested within o (4.5).

Разумеется, в том же документе написано и следующее:

the behavior is undefined if the destructor is invoked for an object whose lifetime has ended

Softwayer ★★
()
Ответ на: комментарий от Softwayer

Мда, забавно. Я приписывал тому анониму то, чего нет. Если ему присваивание nullptr нужно только, чтобы деструктор два раза вызывать, он идиот.

i-rinat ★★★★★
()
Ответ на: комментарий от Softwayer

Не туда показываешь :-) Вот то, что надо:

if the destructor for an automatic object is explicitly invoked, and the block is subsequently left in a manner that would ordinarily invoke implicit destruction of the object, the behavior is undefined

Но ведь это не отменяет нужность impl_ = nullptr :-) Правда, надо писать чуть иначе, т.е. как в GCC :-) Вот так:

  C::~C()
  {
    if (impl_) {
      delete impl_;
      impl_ = nullptr;
    }
  }
И тогда будет всё ок, грамотей :-) Учись :-) Лол :-)

anonymous
()
Ответ на: комментарий от anonymous

Может, расскажешь, как твой if влияет на поведение программы? (если не хочешь в очередной раз продемонстрировать своё невежество, то подсказываю: никак)

Softwayer ★★
()
Ответ на: комментарий от Softwayer

Может, расскажешь, как твой if влияет на поведение программы? (если не хочешь в очередной раз продемонстрировать своё невежество, то подсказываю: никак)

Ты уже своё продемонстрировал :-) Но я то тут причём? :-)

if влияет на поведение программы в том случае, если распределением памяти занимается моя программа, а не стандартный менеджер памяти :-) Можно обеспечить наличие nullptr в impl_, зарезервировав память пока жив хотя бы 1 элемент массива (контейнера), например :-) Ты понимаешь? :-)

anonymous
()
Ответ на: комментарий от anonymous

У вас забыл спросить что мне делать.

RazrFalcon ★★★★★
() автор топика
Ответ на: комментарий от timdorohin

Насколько я понял - это флаги оптимизации. На проверки это никак не влияет.

RazrFalcon ★★★★★
() автор топика
Ответ на: комментарий от i-rinat

Потом упадёт где-то ещё; разбираться придётся.

Для этого (а также других классов ошибок) есть asan

annulen ★★★★★
()
Ответ на: комментарий от anonymous

Их уже раскладывает unique_ptr. По крайней мере, в версии от GCC.

i-rinat ★★★★★
()
Ответ на: комментарий от anonymous

Семантика в цепепе бывает 2-х видов: значений и указателей

Лол.

Ну давай, расскажи, какая ещё в цепепе бывает семантика :-) Лол :-)

anonymous
()
Ответ на: комментарий от anonymous

Ну давай, грамотей, расскажи уже почему компилятор имеет полное право выпиливать присваивание impl_ = nullptr

Почему компилятор имеет полное право выпиливать

2+2;
ты тоже не понимаешь?

anonymous
()
Ответ на: комментарий от anonymous

// сегфолта не будет, спасибо GCC

Ага. Спасибо, что выпилил код, который ничего не делает.

// без этого будет сегфолт при выходе из main()

И в чём разница: есть эта строка https://godbolt.org/g/uDEu6Q или нет её https://godbolt.org/g/Fb9w3k ?

anonymous
()
Ответ на: комментарий от jcd

Проблема в том, что на самом деле ошибка не в switch, а в (Q)3. И компилятору куда лучше было бы ругаться на это. Да и что делать функции с некорретным значением Q? Разве что упасть на assert...

KivApple ★★★★★
()
Ответ на: комментарий от RazrFalcon

Просто меня в своё время сильно удивило, что все «гарантии» const не имеют смысла, ибо ломаются простым кастом.


Какие гарантии? ЕМНИП, const - это просто подсказка компилятору, чтобы он мог лучше оптимизировать код, а не гарантия.

m0rph ★★★★★
()
Ответ на: комментарий от next_time

Якобы потому, что в дестракторе unique_ptr строит такая проверка. Но она есть там потому, что стандарт гарантирует, что deleter не будет вызван, если в unique_ptr нулевой указатель. А deleter может задаваться кастомным, а не сводиться только к вызову delete.

anonymous
()
Ответ на: комментарий от KennyMinigun

Лыбящийся анонимус имел в виду, что сегфолт будет без присвоения nullptr указателю, т.к. delete будет вызван 2 раза с ненулевым указателем.

anonymous
()
Ответ на: комментарий от anonymous

вызван 2 раза с ненулевым указателем.

Одним и тем же, что главное.

anonymous
()
Ответ на: комментарий от anonymous

Чёт я не вкуриваю, мб потому, что ночь не спал.

Делетер не вызывается, т.к. стандарт гарантирует, что deleter не будет вызван, если в unique_ptr нулевой указатель.

C::~C()
  {
      impl_ = nullptr;
      delete impl_;
      impl_ = nullptr;
  }

Делетер не вызывается, т.к. адресс impl_ конвертируется в false

C::~C()
  {
    impl_ = nullptr;
    if (impl_) {
      delete impl_;
      impl_ = nullptr;
    }
  }

Так какая разница?

next_time ★★★★★
()
Последнее исправление: next_time (всего исправлений: 1)
Ответ на: комментарий от next_time

Так какая разница?

Ещё раз: в unique_ptr можно задавать кастомный делетер. Свою функцию/лямбду/класс с оператором(). Которые могут не переваривать вызов их с нулевым указателем, в отличие от дефолтного оператора delete(void*, size_t).

anonymous
()
Ответ на: комментарий от anonymous

ясно, так не «стандарт гарантирует», а делетер по-умолчанию гарантирут, тогда

//спасибо за разъяснения

next_time ★★★★★
()
Ответ на: комментарий от next_time

Нет, именно стандарт гарантирует, что делетер в деструкторе unique_ptr вызовется только не для нулевого указателя.

anonymous
()
Ответ на: комментарий от SZT

Мина состоит в том, что компилятор не ругнётся, если в этот enum добавится ещё значение. А отследить и обновить все возможные использования enum не всегда получается + человеческий фактор.

Поэтому, если default недостижим, то лучше делать exit(1) (в C) или выбрасывать исключение (в плюсах). Как вариант - assert(0)/assert(false).

dzidzitop ★★
()
Последнее исправление: dzidzitop (всего исправлений: 1)
Ответ на: комментарий от dzidzitop

Ну у меня исключения отключены, поэтому unreachable.

RazrFalcon ★★★★★
() автор топика
Ответ на: комментарий от anonymous
  C::~C()
  {
    delete impl_;
    impl_ = nullptr; // твоё удивление :-)
  }
C::~C():
  mov rdi, QWORD PTR [rdi]
  mov esi, 1
  jmp operator delete(void*, unsigned long) ; твоё удивление :-)
anonymous
()
11 мая 2019 г.
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.