Nano Hash - криптовалюты, майнинг, программирование

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

Недавно я наткнулся на следующий код:

class Foo
{
public:
    void bar();
    // .. other stuff
};

void Foo::bar()
{
    if(!this) {
        // .. do some stuff without accessing any data members
        return;
    }

    // .. do normal actions using data members
}

Код компилируется, потому что в C ++ методы - это просто функции, которым неявно передается указатель на 'this', и 'this' можно проверить на значение NULL, как и любой другой указатель. Очевидно, что этот код сбивает с толку и является плохой практикой, хотя он и не дает сбоев; было бы довольно запутать пошаговое выполнение кода в отладчике, увидеть, что указатель NULL вот-вот вызовет метод, а затем не увидит ожидаемого сбоя. Мой вопрос: нарушает ли стандарт C ++ вызов SomeFooPtr->bar() где SomeFooPtr == NULL?

Мне приходит в голову, что это может быть не так, потому что определенный пользователем оператор-> возвращает указатель, что означает, что даже если этот указатель имеет значение NULL, он определенно не разыменовывался (разыменование указателя NULL, я уверен, рассматривается стандартом как незаконный или неопределенный). С другой стороны, семантика необработанных указателей не обязательно должна соответствовать семантике указателей, определенных пользователем - возможно, оператор operator-> на них считается разыменованием, даже если компилятор не сгенерирует его.



Ответы:


1

Это, вероятно, будет работать в большинстве систем, но это Undefined Behavior. Сказал Стандарт:

5.2.5.3

Если E1 имеет тип «указатель на класс X», тогда выражение E1->E2 преобразуется в эквивалентную форму (*(E1)).E2 [...]

И:

5.2.5.1

Постфиксное выражение, за которым следует точка . или стрелка ->, за которым может следовать ключевое слово template (14.8.1), за которым следует id-выражение, является постфиксным выражением. Выражается постфиксное выражение перед точкой или стрелкой; 58) [...]

58) Эта оценка происходит, даже если результат не нужен для определения значения всего постфиксного выражения, например, если id-выражение обозначает статический член.

Оценка *x, где x - нулевой указатель, приводит к неопределенному поведению, так что ваш случай явно относится к UB еще до того, как функция будет введена.

15.07.2010
  • Спасибо, это именно то, что я искал. 15.07.2010
  • Извините за грязные правки. Сначала я что-то пропустил в Стандарте, потом неправильно понял ваш вопрос. Теперь все должно быть в порядке. 15.07.2010
  • Не могли бы вы привести пример выражения вида A.template B? Где уместен этот синтаксис? 15.07.2010
  • @Philip: Допустим, вы пишете код внутри шаблонного класса, который принимает T. Затем внутри этого класса вы создаете объект some_other_class ‹T›. Затем оказывается, что some_other_class ‹T› имеет шаблонную функцию-член foo. some_other_class ‹T› x; x.template foo () ;. По сути, когда вы вводите параметр шаблона в другой шаблон, если последний шаблон сам имеет шаблонный элемент, вы должны поместить туда дополнительное ключевое слово шаблона при доступе к нему, чтобы не запутать синтаксический анализатор. По крайней мере, я так думаю. Сейчас я делаю это интуитивно, поэтому, возможно, я неправильно это описываю. 15.07.2010
  • @Philipp: Какое совпадение, я только что узнал об этом синтаксисе несколько минут назад! Здесь: stackoverflow.com/questions/3257890/ 15.07.2010
  • @ Джозеф Гарвин: Это немного запутанное объяснение, особенно в вашем случае some_other_class ‹T› x; x.template foo (); ключевое слово шаблона не требуется. По сути, вам нужно ключевое слово template, чтобы различать переменные-члены и шаблоны-элементы зависимых типов. Имена в зависимых типах интерпретируются анализатором как переменные (или функции), поэтому, если T является параметром шаблона (не фактическим типом), например. other_class ‹T› :: foo ‹int› () будет интерпретироваться как other_class ‹T› :: foo is_less_than int ... В этом случае вы должны использовать ключевое слово template, чтобы сообщить компилятору, что foo является шаблоном. 15.07.2010

  • 2

    Этот тест не работает, даже если разыменование не было UB. Он ломается, когда в игру вступают this-настройки для множественного наследования:

    #include <stdio.h>
    class B
    {
        int value;
        public:
        void foo()
        {
            if (!this)
                printf("this==0\n");
            else 
                printf("safe\n");
        }
    };
    class A { public: int anotherValue; };
    class Z : public A,public B {};
    
    int main()
    {
        Z *z=0;
        z->foo();
    }
    

    печатает здесь "безопасно".

    15.07.2010

    3

    Это (теперь все вместе) неопределенное поведение. Однако для многих компиляторов это будет работать с дополнительным ограничением, заключающимся в том, что метод не должен быть виртуальным.

    15.07.2010

    4

    Это УБ. Хороший способ вызвать сбой - использовать его в качестве базового класса производного класса, который использует множественное наследование. YMMV.

    15.07.2010

    5

    Неважно, законно ли это, это сбивает читателя с толку. В реализации, в которой работает этот код, доступ к vtable осуществляется по типу, а не по объекту.

    Более того, я ожидаю, что этот код был добавлен, чтобы скрыть сбой конструктора, который скроет множество проблем в другом месте. Отказ конструктора должен обрабатываться правильно, а не с неприятной кладжем, как в примере.

    15.07.2010
    Новые материалы

    Кластеризация: более глубокий взгляд
    Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

    Как написать эффективное резюме
    Предложения по дизайну и макету, чтобы представить себя профессионально Вам не позвонили на собеседование после того, как вы несколько раз подали заявку на работу своей мечты? У вас может..

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

    Как я автоматизирую тестирование с помощью Jest
    Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

    Понимание расстояния Вассерштейна: мощная метрика в машинном обучении
    В обширной области машинного обучения часто возникает необходимость сравнивать и измерять различия между распределениями вероятностей. Традиционные метрики расстояния, такие как евклидово..

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..