This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of New status.
shared_ptr
Section: 32.5.2 [atomics.syn] Status: New Submitter: Casey Carter Opened: 2019-06-11 Last modified: 2020-09-06
Priority: 3
View other active issues in [atomics.syn].
View all other issues in [atomics.syn].
View all issues with New status.
Discussion:
This well-formed C++14 program:
#include <atomic> #include <memory> struct Abstract { virtual void test() = 0; }; struct Concrete : Abstract { virtual void test() override {} }; int main() { std::shared_ptr<Abstract> ptr; std::atomic_store<Abstract>(&ptr, std::make_shared<Concrete>()); }
is ill-formed in C++17. P0558 changed the non-member
non-shared_ptr
atomic functions to avoid deducing from their second argument,
e.g. C++14 atomic_store
:
template<class T> void atomic_store(atomic<T>*, T); // #1
became C++17 atomic_store
:
template<class T> void atomic_store(atomic<T>*, typename atomic<T>::value_type); // #2
The program intends to call the "other" atomic_store
from
99 [depr.util.smartptr.shared.atomic]:
template<class T> void atomic_store(shared_ptr<T>*, shared_ptr<T>); // #3
In C++14, the call expression in the sample program —
std::atomic_store<Abstract>(&ptr, std::make_shared<Concrete>())
—
selects overload #3
; overload #1
fails to be viable due to the
lack of conversions from shared_ptr<Abstract>*
to atomic<Abstract>*
and from shared_ptr<Concrete>
to Abstract
. In C++17, overload
#2
doesn't get to the point of considering argument conversions: when we try to
generate the declaration of the specialization for T = Abstract
we must instantiate
atomic<Abstract>
in order to substitute typename atomic<Abstract>::value_type
,
but doing so violates the requirement in [atomics.types.generic] p1 that "The type of the template
argument T
shall be trivially copyable"
atomic<T>::value_type
is always an alias
for T
: for those non-member atomic functions with overloads defined in
99 [depr.util.smartptr.shared.atomic], use a different form to require that T
in the type of the second parameter is non-deduced.
[2019-07 Issue Prioritization]
Priority to 3 after discussion on the reflector.
Proposed resolution:
This wording is relative to N4810.
Modify 32.5.2 [atomics.syn], header <atomic>
synopsis, as indicated:
[…] // 32.5.9 [atomics.nonmembers], non-member functions […] template<class T> void atomic_store(volatile atomic<T>*,typename atomic<T>::value_typetype_identity_t<T>) noexcept; template<class T> void atomic_store(atomic<T>*,typename atomic<T>::value_typetype_identity_t<T>) noexcept; template<class T> void atomic_store_explicit(volatile atomic<T>*,typename atomic<T>::value_typetype_identity_t<T>, memory_order) noexcept; template<class T> void atomic_store_explicit(atomic<T>*,typename atomic<T>::value_typetype_identity_t<T>, memory_order) noexcept; […] template<class T> T atomic_exchange(volatile atomic<T>*,typename atomic<T>::value_typetype_identity_t<T>) noexcept; template<class T> T atomic_exchange(atomic<T>*,typename atomic<T>::value_typetype_identity_t<T>) noexcept; template<class T> T atomic_exchange_explicit(volatile atomic<T>*,typename atomic<T>::value_typetype_identity_t<T>, memory_order) noexcept; template<class T> T atomic_exchange_explicit(atomic<T>*,typename atomic<T>::value_typetype_identity_t<T>, memory_order) noexcept; template<class T> bool atomic_compare_exchange_weak(volatile atomic<T>*,typename atomic<T>::value_typetype_identity_t<T>*,typename atomic<T>::value_typetype_identity_t<T>) noexcept; template<class T> bool atomic_compare_exchange_weak(atomic<T>*,typename atomic<T>::value_typetype_identity_t<T>*,typename atomic<T>::value_typetype_identity_t<T>) noexcept; template<class T> bool atomic_compare_exchange_strong(volatile atomic<T>*,typename atomic<T>::value_typetype_identity_t<T>*,typename atomic<T>::value_typetype_identity_t<T>) noexcept; template<class T> bool atomic_compare_exchange_strong(atomic<T>*,typename atomic<T>::value_typetype_identity_t<T>*,typename atomic<T>::value_typetype_identity_t<T>) noexcept; template<class T> bool atomic_compare_exchange_weak_explicit(volatile atomic<T>*,typename atomic<T>::value_typetype_identity_t<T>*,typename atomic<T>::value_typetype_identity_t<T>, memory_order, memory_order) noexcept; template<class T> bool atomic_compare_exchange_weak_explicit(atomic<T>*,typename atomic<T>::value_typetype_identity_t<T>*,typename atomic<T>::value_typetype_identity_t<T>, memory_order, memory_order) noexcept; template<class T> bool atomic_compare_exchange_strong_explicit(volatile atomic<T>*,typename atomic<T>::value_typetype_identity_t<T>*,typename atomic<T>::value_typetype_identity_t<T>, memory_order, memory_order) noexcept; template<class T> bool atomic_compare_exchange_strong_explicit(atomic<T>*,typename atomic<T>::value_typetype_identity_t<T>*,typename atomic<T>::value_typetype_identity_t<T>, memory_order, memory_order) noexcept;
Modify 32.5.9 [atomics.nonmembers] as indicated:
-1- A non-member function template whose name matches the pattern
atomic_f
or the patternatomic_f_explicit
invokes the member functionf
, with the value of the first parameter as the object expression and the values of the remaining parameters (if any) as the arguments of the member function call, in order. An argument for a parameter of typeatomic<T>::value_type*
ortype_identity_t<T>*
is dereferenced when passed to the member function call. If no such member function exists, the program is ill-formed.