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.

### 3220. P0558 broke conforming C++14 uses of atomic `shared_ptr`

Section: 33.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 D.20 [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"

The fix is fairly straightforward since `atomic<T>::value_type` is always an alias for `T`: for those non-member atomic functions with overloads defined in D.20 [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.

1. Modify 33.5.2 [atomics.syn], header `<atomic>` synopsis, as indicated:

   >     […]
   >     // 33.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;

2. Modify 33.5.9 [atomics.nonmembers] as indicated:

   > -1- A non-member function template whose name matches the pattern `atomic_f` or the pattern `atomic_f_explicit` invokes the member function `f`, 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 type `atomic<T>::value_type*` or `type_identity_t<T>*` is dereferenced when passed to the member function call. If no such member function exists, the program is ill-formed.