Проверьте, не инициализирован ли атом c ptr в g cc 4.4.7 (без nullptr) - PullRequest
1 голос
/ 10 марта 2020

Во время перефакторизации одноэлементного класса для обеспечения безопасности потоков (, следуя советам Херба Саттера, как написать правильную двойную проверку блокировки ), я столкнулся с проблемой с моей версией компилятора (g cc 4.4. 7 с флагом --std = c ++ 0x), поддерживающим атомы, но не поддерживающим nullptr.

Текущий код

class SingletonClass {
   public:
SingletonClass* getInstance() {
    if (instance == NULL) {
        instance == new SingletonClass();
    }
    return instance;
}

   private:
SingletonClass() = default;
~SingletonClass() = default;

static SingletonClass* instance;};

Чего я хотел бы достичь

#include <cstdatomic>
#include <mutex>

class SingletonClass {
   public:
SingletonClass* getInstance() {
    if (instance == NULL) {
        std::lock_guard<std::mutex> lock (m);
        if(instance == NULL){
           instance = new SingletonClass();
        }
    }
    return instance;
}

   private:
 SingletonClass() = default;
~SingletonClass() = default;

static std::atomic<SingletonClass*> instance;
static std::mutex m;
};

Но это дает мне ошибку, говоря, что нет оператора для компиляции atomi c ptr в NULL

main.cpp: In member function ‘SingletonClass* SingletonClass::getInstance()’:
main.cpp:7: error: ambiguous overload for ‘operator==’ in ‘SingletonClass::instance == 0l’
main.cpp:7: note: candidates are: operator==(void*, void*) <built-in>
main.cpp:7: note:                 operator==(SingletonClass*, SingletonClass*) <built-in>

Поскольку я не могу ни принудительно привести ptr экземпляра к NULL, ни использовать nullptr, как мне обойти его и проверить, инициализирован ли он или нет?

Ответы [ 2 ]

1 голос
/ 10 марта 2020

Вы можете использовать неявное преобразование указателя в bool:

SingletonClass* getInstance() {
    if (instance.load()) {
        std::lock_guard<std::mutex> lock (m);
        if(instance.load()){
           instance.store(new SingletonClass());
        }
    }
    return instance;
}

Просмотреть его онлайн

Обратите внимание, что неявное преобразование из std::atomic<SingletonClass*> в SingletonClass* возможно, но неоднозначно в этом контексте. Кроме того, само присвоение неоднозначно, поэтому был добавлен вызов store().


Однако, возможно, решение более простое - зачем вообще нужен std::atomic? Вы уже блокируете доступ к сохраненному указателю, поэтому вы в безопасности:

#include <mutex>

class SingletonClass {
public:
    SingletonClass* getInstance() {
        std::lock_guard<std::mutex> lock (m);
        if (instance == NULL) {
            instance = new SingletonClass();
        }
        return instance;
    }

private:
    SingletonClass() = default;
    ~SingletonClass() = default;

    static SingletonClass* instance;
    static std::mutex m;
};

std::atomic используется для доступа без блокировки (или, по крайней мере, с доступом к скрытой блокировке). Я не могу придумать причину использовать их вместе на макушке головы.

Мьютекс здесь в значительной степени необходим - вы хотите заблокировать целую функцию как критическую секцию, иначе два потока могут создать два одноэлементные объекты, и один будет протекать.

0 голосов
/ 10 марта 2020

Редактировать: Я думаю, что ответ Иксисарвинена выше, лучше. У вас уже есть замок, не беспокойтесь больше. Оставляя мой старый ответ ниже.

Я не уверен насчет конкретной версии c G CC, но если теоретически есть <atomic>, это должно работать:

SingletonClass* getInstance() {
    if (instance.load() == NULL) {
        std::lock_guard<std::mutex> lock (m);
        if(instance.load() == NULL){
            instance = new SingletonClass();
        }
    }
    return instance;
}

Согласно документация , load() должна давать вам то, что вы хотите, сам указатель для сравнения, с которым должен работать с NULL.

Удачи на старых компиляторы. Мне тоже приходилось делать это иногда из-за требований клиентов.

...