Title
P0558 broke conforming C++14 uses of atomic shared_ptr
Status
new
Section
[atomics.syn]
Submitter
Casey Carter

Created on 2019-06-11.00:00:00, last changed 2019-07-23.15:26:26.

Messages

Date: 2019-07-23.15:26:26

Proposed resolution:

This wording is relative to N4810.

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

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

Date: 2019-07-23.15:26:26

[ 2019-07 Issue Prioritiztion ]

Priority to 3 after discussion on the reflector.

Date: 2019-06-11.00:00:00

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 [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 [depr.util.smartptr.shared.atomic], use a different form to require that T in the type of the second parameter is non-deduced.

History
Date User Action Args
2019-07-23 15:26:26adminsetmessages: + msg10502
2019-06-16 09:24:41adminsetmessages: + msg10448
2019-06-11 00:00:00admincreate