For std::atomic the standard says that (emphasis mine)
The primary std::atomic template may be instantiated with any TriviallyCopyable type T:
struct Counters { int a; int b; }; // user-defined trivially-copyable type
std::atomic<Counters> cnt;         // specialization for the user-defined type
So you can just create a struct of 2 uint64_t like this
struct atomic128 {
    uint64_t a1, a2;
};
which is trivially copyable (it's easy to confirm with std::is_trivially_copyable), and then use std::atomic<atomic128>. You'll get an error if std::atomic<type> is not trivially copyable
That way the compiler will automatically use lock-free updating mechanism if it's available. No need to do anything special, just check that with either of the below if necessary
All atomic types except for std::atomic_flag may be implemented using mutexes or other locking operations, rather than using the lock-free atomic CPU instructions. Atomic types are also allowed to be sometimes lock-free: for example, if only some subarchitectures support lock-free atomic access for a given type (such as the CMPXCHG16B instruction on x86-64), whether atomics are lock-free may not be known until runtime.
std::atomic_is_lock_free and std::atomic::is_lock_free
Here's a demo on Compiler Explorer. As you can see a lock cmpxchg16b is emitted, although GCC 7 and up will just call __atomic_store_16 which internally use cmpxchg16b if it's available
On some platforms long double is a 128-bit type or is padded to 128 bits, therefore std::atomic<long double> may be another solution, but of course you need to check its size and whether it's lock-free or not first
Another alternative is Boost.Atomic. It also has the macros BOOST_ATOMIC_INT128_LOCK_FREE and BOOST_ATOMIC_LONG_DOUBLE_LOCK_FREE to check
On some CPUs 128-bit SSE operations are also atomic, unfortunately there's no way to check whether you can use that or not
See also: