Соответствует ли 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, который просто ведет себя так, как ожидалось?
Спасибо заранее!
Ответы
Ответ 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" и т.д. Он не может быть обновлен атомарно, отсюда и странные результаты.
Поскольку приведенный вами пример представляет собой последовательный код, этого не происходит.