странная оптимизация шаблона c ++ - PullRequest
0 голосов
/ 28 августа 2018

Я написал шаблон шаблона синглтона так же, как Boost:

template <typename _T>
class Singleton
{ 
    public :
    static _T* Instance()
    {
        static _T obj;
        return &obj;
    }

protected :
    Singleton() {}

private :
    struct ObjectCreator
    {
        ObjectCreator()
        {
            Singleton<_T>::instance();
        }
    };

    static ObjectCreator object_creator;
};

template <typename _T>
typename Singleton<_T>::ObjectCreator Singleton<_T>::object_creator;

И я написал основную функцию для проверки.

#include "Singleton.h"
class A : public Singleton <A>
{
    public:
        int a;
};


int main()
{
    A::Instance()->a = 2;
}

Я знаю, что опечатал Instance в конструкторе ObjectCreator, странно то, что я могу правильно скомпилировать его с помощью gcc-4.4.7, затем я использовал clang-6.0, он ударил меня опечаткой.

Я думаю, что gcc может выполнить некоторую оптимизацию, потому что я ничего не делал с ObjectCreator, поэтому он игнорировал код ошибки.

У меня есть два вопроса:

  1. Что я должен сделать, чтобы gcc сообщил мне об этой ошибке ( без изменения моего кода ), например, добавил флаг компилятора?
  2. Если у кого-нибудь есть более надежное объяснение этому? Какой-нибудь официальный документ подойдет.

PS: я знаю, что boost добавит функцию do_nothing в ObjectCreate и вызовет ее с Singleton<_T>:: Instance(), чтобы избежать этой оптимизации.

Ответы [ 2 ]

0 голосов
/ 28 августа 2018

Если я правильно прочитал стандарт, я не верю, что ваш код неверен (за исключением использования идентификатора _T). Пробежать лишнюю милю - это фантастика, но GCC не ошибается, принимая это как есть.

Причина в том, что ваша программа содержит только неявную реализацию вашего шаблона. Согласно N1905 1 (выделено мое):

14.7.1 Неявная реализация [temp.inst]

1 ... Неявное создание экземпляра класса специализация шаблона вызывает неявную реализацию объявления , но не определений или аргументов по умолчанию функции-члены класса, классы-члены , члены-статические данные и член шаблоны; и это вызывает неявную реализацию определений член анонимных союзов. Если только член шаблона класса или шаблон члена был явно создан или явно специализация, специализация члена неявно создается, когда на специализацию ссылаются в контексте, требует, чтобы определение члена существовало; в частности, инициализация (и любые связанные побочные эффекты) статических данных член не происходит, если статический член данных сам не используется в способ, который требует определения статического члена данных.

Ничего использует object_creator так, чтобы его определение существовало. Как таковая только декларация когда-либо проверяется. Кроме того, требуется создать только само объявление class ObjectCreator, а не его определение (или определение его конструктора). По этой же причине можно определить переменную extern типа класса, объявленного вперед:

extern class C c;

Приведенный выше абзац (и тот факт, что ничто не использует object_creator) требует только создания экземпляра имени типа и имени объекта для получения эффекта, аналогичного описанному выше внешнему объявлению.

В результате GCC никогда не должен проверять, instance действителен. Я бы сказал, что, учитывая вышеприведенный абзац, даже если у вас не было опечатки, вполне возможно, object_creator не делает то, что вы думаете, что делает. Если ваш код работал, то только потому, что функция local static obj была инициализирована при первом использовании (что делает ObjectCreator избыточным).

Что касается того, почему добавление явного экземпляра (как @ P я предложил ) немедленно вызывает ошибку. Мы можем видеть здесь:

14.7.2 Явная реализация [temp.explicit]

7 Явная реализация шаблона класса Специализация также явно создает экземпляр каждого из его членов (не включая членов, унаследованных от базовых классов), чье определение видимый в момент создания экземпляра и который ранее не был явно специализируется на единице перевода, содержащей явное конкретизации.

Когда мы делаем это, мы рекурсивно вынуждаем создавать все экземпляры и, в результате, проверять.


1 - это проект 2005 года. Очень близко к C ++ 03, и поэтому я считаю целесообразным, учитывая ваше использование GCC 4.4.7.

0 голосов
/ 28 августа 2018
  • Что я должен сделать, чтобы gcc сообщил об этой ошибке (без изменения моего кода), например, добавил флаг компилятора?

Вы можете добавить явное создание экземпляра template class Singleton<float>; (я просто случайно выбрал float как тип, но вы можете выбрать что-нибудь более подходящее), чтобы заставить GCC проверять синтаксис. См. https://gcc.godbolt.org/z/ii43qX для примера.

Если вы просто хотите проверить, вы также можете поместить этот явный экземпляр в отдельную единицу компиляции, добавив другой cpp-файл в ваш проект.

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

  • Если у кого-нибудь есть более надежное объяснение этому? Какой-нибудь официальный документ подойдет.

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

@ StoryTeller нашел правильный абзац в стандарте

14.7.1 Неявная реализация [temp.inst]

Неявное создание экземпляров специализации шаблона класса вызывает неявное создание объявлений, но не определений или аргументов по умолчанию, функций-членов класса, классов-членов, статических членов-данных и шаблонов элементов; и это вызывает неявную реализацию определений членских анонимных союзов. Если элемент шаблона класса или шаблон элемента не был явно создан или явно специализирован, специализация члена создается неявно, когда на специализацию ссылаются в контексте, который требует определения элемента; в частности, инициализация (и любые связанные побочные эффекты) элемента статических данных не происходит, если только сам элемент статических данных не используется таким образом, который требует определения элемента статических данных.

EDIT Вы должны принять ответ @StoryTeller, поскольку он правильно объяснил оба аспекта вашего вопроса.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...