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?