Why use std::forward in concepts?

Viewed 831

I was reading the cppreference page on Constraints and noticed this example:

// example constraint from the standard library (ranges TS)
template <class T, class U = T>
concept bool Swappable = requires(T t, U u) {
    swap(std::forward<T>(t), std::forward<U>(u));
    swap(std::forward<U>(u), std::forward<T>(t));
};

I'm puzzled why they're using std::forward. Some attempt to support reference types in the template parameters? Don't we want to call swap with lvalues, and wouldn't the forward expressions be rvalues when T and U are scalar (non-reference) types?

For example, I would expect this program to fail given their Swappable implementation:

#include <utility>

// example constraint from the standard library (ranges TS)
template <class T, class U = T>
concept bool Swappable = requires(T t, U u) {
    swap(std::forward<T>(t), std::forward<U>(u));
    swap(std::forward<U>(u), std::forward<T>(t));
};

class MyType {};
void swap(MyType&, MyType&) {}

void f(Swappable& x) {}

int main()
{
    MyType x;
    f(x);
}

Unfortunately g++ 7.1.0 gives me an internal compiler error, which doesn't shed much light on this.

Here both T and U should be MyType, and std::forward<T>(t) should return MyType&&, which can't be passed to my swap function.

Is this implementation of Swappable wrong? Have I missed something?

2 Answers
Related