CV-qualifiers & references for concept requires parameters

Viewed 62

Suppose I have a deserialize function:

template <typename T>
T deserialize(const std::string& src);

// Specialized for various types
// ...

and want to define a Deserializable concept for types for which deserialize is specialized. Are there any difference between the following versions?

template <typename T>
concept Deserializable = requires(std::string s) { deserialize<T>(s); };
// concept Deserializable = requires(std::string& s) { deserialize<T>(s); };
// concept Deserializable = requires(const std::string s) { deserialize<T>(s); };
// concept Deserializable = requires(const std::string& s) { deserialize<T>(s); };

If requires was similar to a normal function, then I'd expect the 1st (resp. 3rd) version to be identical to the 2nd (resp. 4th) version, as the name s inside a normal function body is an lvalue. However, I've seen the standard library use reference types as requires parameters, for example here:

template< class T >
concept range = requires(T& t) {
  ranges::begin(t); // equality-preserving for forward iterators
  ranges::end  (t);
};

which seems to suggest that reference vs. value matters.

More generally, do CV-qualifiers and references for requires parameters matter?

UPDATE: I came up with a test to show that requires indeed seems to behave like normal functions:

template <typename T>
void f(T&&, T&&) {}

template <typename T>
concept Foo = requires(int x, int& y, int&& z) {
    f(x, y);
    f(x, z);
    f(x, 123);  // won't compile: deduced int& and int
};

int main()
{
}

So it seems the requires(T& t) in the standard library example above is no different than requires(T t). Is this always the case?

1 Answers

If T is an rvalue reference type U&&, T& is U&, but that matters only if decltype or something like std::forward is used. The only other formal difference is that it prevents the parameter from being adjusted to be a pointer if it’s an array or function type, which also rarely matters.

It may also be meant to avoid suggesting that a type is copyable/movable (as is often required for by-value function parameters).

Related