Подтвердить что ты не робот

Соответствует ли std:: atomic <std::string>?

Я читаю через Энтони Уильямса "С++ Concurrency в действии" и в главе 5, где рассказывается о новой модели памяти с многопотоковой памятью и атомных операциях, и он утверждает:

Чтобы использовать std::atomic<UDT> для определенного пользовательского UDT, этот тип должен иметь тривиальный оператор присваивания копии.

Как я понимаю, это означает, что мы можем использовать std::atomic<UDT>, если следующее возвращает true:

std::is_trivially_copyable<UDT>::value

По этой логике мы не сможем использовать std::string в качестве аргумента шаблона для std::atomic и корректно работать.

Однако следующий код компилируется и запускается с ожидаемым выходом:

#include <atomic>
#include <thread>
#include <iostream>
#include <string>

int main()
{
    std::atomic<std::string> atomicString;

    atomicString.store( "TestString1" );

    std::cout << atomicString.load() << std::endl;

    atomicString.store( "TestString2" );

    std::cout << atomicString.load() << std::endl;

    return 0;
}

Является ли это примером поведения undefined, который просто ведет себя так, как ожидалось?

Спасибо заранее!

4b9b3361

Ответ 1

В стандарте не указывается специализация std::atomic<std::string>, поэтому применяется общий template <typename T> std::atomic<T>. 29.5 [atomics.types.generic] p1:

Существует общий шаблонный шаблон. Тип аргумента шаблона T должен быть тривиально скопируемым (3.9).

Нет заявлений о том, что реализация должна диагностировать нарушения этого требования. Таким образом, либо (a) использование std::atomic<std::string> вызывает undefined поведение, либо (b) ваша реализация предоставляет std::atomic<std::string> как соответствующее расширение.

Рассматривая страницу MSDN для std::atomic<T> (http://msdn.microsoft.com/en-us/library/vstudio/hh874651.aspx), в ней явно указывается требование, чтобы T было тривиально с возможностью копирования, и он не говорит ничего конкретного о std::atomic<std::string>. Если это расширение, оно недокументировано. Мои деньги находятся в режиме undefined.

В частности, 17.6.4.8/1 применяется (с благодарностью Даниэлю Крюглеру за установку меня прямо):

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

std::string, конечно, не соответствует требованию std::atomic<T>, чтобы параметр шаблона T был тривиально скопируемым, поэтому стандарт не предъявляет никаких требований к реализации. В качестве проблемы с качеством реализации обратите внимание на то, что static_assert(std::is_trivially_copyable<T>::value, "std::atomic<T> requires T to be trivially copyable"); является простой диагностикой для обнаружения этого нарушения.


2016-04-19 Обновление: я не знаю, когда произошло изменение, но VS2015 Update 2 теперь диагностирует std::atomic<std::string>:

error C2338: atomic requires T to be trivially copyable.

Ответ 2

Нет, это поведение undefined. Более того, поскольку std::string не является тривиально скопируемым, соответствующий компилятор должен был выдавать "хотя бы одно диагностическое сообщение":

29.5 Атомные типы

Существует общий шаблонный шаблон. Тип аргумента шаблона T должен быть тривиально скопируемым (3.9).

1.4 Выполнение выполнения

- Если программа содержит нарушение любого диагностируемого правила [...] реализация должна выпустить хотя бы одно диагностическое сообщение.

Ответ 3

Почему вы думаете, что это будет работать "правильно", когда есть несколько потоков, пытающихся прочитать/записать std::atomic<std::string>?

Это С++, вам определенно разрешено стрелять в ногу. Если вы хотите использовать тип, который не удовлетворяет требованиям, которые вы можете использовать, компилятор "может" (не будет!) Останавливать вас, но вы начнете видеть странное/необъяснимое поведение в какой-то момент, когда несколько потоков попытаются прочитать/напишите строку.

Это требование состоит в том, чтобы гарантировать атомарность чтения и записи, если объект не является тривиально скопируемым, а затем визуализировать эту сцену: в строке была "Старая ценность". 1 Writer выдает .store( "Новые данные" ), теперь есть еще один поток, который выдает .load() в той же переменной, теперь без свойства trivially_copyable, поток читателя может видеть "Nld Value" или "New Value" и т.д. Он не может быть обновлен атомарно, отсюда и странные результаты.

Поскольку приведенный вами пример представляет собой последовательный код, этого не происходит.