c++c++17swap

Are the Swappable Requirements and the Type Trait std::is_swappable<> equivalent in C++17?


#include <type_traits>

struct E {
    int x;

    E(   ) = default;
    E(E&&) = delete;
    E& operator=(E&&) = delete;
};

void swap(E& a, E& b) { /* Does NOT actually swap */ }

int main(){
    static_assert( std::is_swappable_v<E> );
}

The above class E is neither MoveConstructible nor MoveAssignable. Thus, the <utility> library swap() function cannot be used (C++17:23.2.3, paragraph 3). Instead, a local custom swap() function is defined. However, it does not actually perform the swap operation.

In this context, the type trait std::is_swappable<> (C++17:23.15.4.3) yields true. Nevertheless, the Swappable requirements seem to be not fulfilled, looking at C++17:20.5.3.2, paragraph 2:

An object t is swappable with an object u if and only if:

  • the expressions swap(t, u) and swap(u, t) are valid when evaluated in the context described below, and
  • these expressions have the following effects:
    • the object referred to by t has the value originally held by u and
    • the object referred to by u has the value originally held by t.

The expressions swap(e1,e2) and swap(e2,e1) are valid for two objects of type E, but they don't have the stated effects. Is the meaning of std::is_swappable<> not exactly the same as fulfilling the Swappable requirements? Or is the compiler (and the <type_traits> library) trusting the user to implement the custom swap() function with those stated effects?


Solution

  • Or is the compiler (and the <type_traits> library) trusting the user to implement the custom swap() function with those stated effects?

    That's true for all semantic requirements on user-supplied types/functions. It is fundamentally not possible for the compiler to verify these semantics.

    If you supply a library functionality with a swap implementation that isn't actually satisfying the semantics required by the library, then your program simply has undefined behavior.

    All that can be checked, to varying degrees, are syntactic requirements. Type traits like std::is_swappable can only check that.

    Library functionality that requires the Swappable concept might not even check the syntactic requirements though. For example, calling std::sort with an iterator value type that doesn't satisfy Swappable is also just undefined behavior, not a guaranteed compile time diagnosis, even if it doesn't satisfy std::is_swappable.