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?
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
With forwarding functions using a template parameter, the idiom is to forward using the typename T, e.g.
template <typename T>
void fwd(T &&x) {
some_func(std::forward<T>(x));
}
Equivalent approach when using a forwarding function (or lambda) that takes an auto type parameter:
void fwd(auto &&x) {
some_func(std::forward<decltype(x)>(x));
}
In the above decltype(x) is not necessarily the same as the type of T in the previous example. E.g. fwd(3) deduces T to int while decltype(x) is int&&.
Can someone explain why we use T in one case vs. decltype(x) as the template parameter to std::forward above? Is the end result always guaranteed to be identical based on the implementation of std::forward?
void fwd(auto &&x) is a c++20 shorthand for template <typename T> void fwd(T &&x). But in this case you don't have type to refer to. You can write this:
void fwd(auto &&x) {
using T = std::remove_reference_t<decltype(x)>;
some_func(std::forward<T>(x));
}
Here T equals to T in original version, but it doesn't matter much.
std::forward<T> and std::forward<std::remove_reference_t<T>> are equal, because its signature defined like this:
template< class T >
constexpr T&& forward( std::remove_reference_t<T>&& t ) noexcept;
I think the problem is fairly common, so there should be a known solution. I came up with one, but I'm not really satisfied, so I'm asking here, hoping someone can help.
Say I have a function, whose signature is
template<typename T>
void foo(const MyArray<const T>& x);
The const in the template parameter is to prevent me from changin the array content, since (for reasons beyond this question), the accessors ([] and ()) of MyArray<T> are always marked const, and return references to T (hence, the const ensure safety, since MyArray<T>::operator[] returns T&, while MyArray<const T>::operator[] returns const T&).
Great. However, templates with different template arguments are non related, so I can't bind a reference to MyClass<T> to a reference of MyClass<const T>, meaning I can't do this
MyArray<double> ar(/*blah*/);
foo(ar);
Notice that, without a reference, the code above would work provided that there is a copy constructor that lets me create MyArray<const T> from MyArray<T>. However, I don't want to remove the reference, since the array construction would happen a lot of times, and, despite relatively cheap, its cost would add up.
So the question: how can I call foo with an MyArray<T>?
My only solution so far is the following:
MyArray<T> ar(/*blah*/);
foo(reinterpret_cast<MyArray<const T>>(ar));
(actually in my code I hid the reinterpret cast in an inlined function with more verbose name, but the end game is the same). The class MyArray does not have a specialization for const T that makes it not reinterpretable, so the cast should be 'safe'. But this is not really a nice solution to read. An alternative, would be to duplicate foo's signature, to have a version taking MyArray<T>, which implementation does the cast and calls the const version. The problem with this is code duplication (and I have quite a few functions foo that need to be duplicated).
Perhaps some extra template magic on the signature of foo? The goal is to pass both MyArray<T> and MyArray<const T>, while still retaining const-correctness (i.e., make the compiler bark if I accidentally change the input in the function body).
Edit 1: The class MyArray (whose implementation is not under my control), has const accessors, since it stores pointers. So calling v[3] will modify the values in the array, but not the members stored in the class (namely a pointer and some smart-pointer-like metadata). In other words, the object is de facto not modified by accessors, though the array is. It's a semantic distinction. Not sure why they went this direction (I have an idea, but too long to explain).
Edit 2: I accepted one of the two answers (though they were somewhat similar). I am not sure (for reasons long to explain) that the wrapper class is doable in my case (maybe, I have to think about it). I am also puzzled by the fact that, while
template<typename T>
void foo(const MyArray<const T>& x);
MyArray<int> a;
foo(a);
does not compile, the following does
void foo(const MyArray<const int>& x);
MyArray<int> a;
foo(a);
Note: MyArray does offer a templated "copy constructor" with signature
template<typename S>
MyArray(const MyArray<S>&);
so it can create MyArray<const T> from MyArray<T>. I am puzzled why it works when T is explicit, while it doesn't if T is a template parameter.
I would stay with
template<typename T>
void foo(const MyArray<T>&);
and make sure to instantiate it with const T (in unitTest for example).
Else you can create a view as std::span.
Something like (Depending of other methods provided by MyArray, you probably can do a better const view. I currently only used operator[]):
template <typename T>
struct MyArrayConstView
{
MyArrayConstView(MyArray<T>& array) : mArray(std::ref(array)) {}
MyArrayConstView(MyArray<const T>& array) : mArray(std::ref(array)) {}
const T& operator[](std::size_t i) {
return std::visit([i](const auto& a) -> const T& { return a[i]; }), mArray);
}
private:
std::variant<std::reference_wrapper<MyArray<T>>,
std::reference_wrapper<MyArray<const T>>> mArray;
};
and then
template <typename T>
void foo(const MyArrayConstView<T>&);
but you need to call it explicitly, (as deduction won't happen as MyArray<T> is not a MyArrayConstView)
MyArray<double> ar(/*blah*/);
foo(MyArrayConstView{ar});
foo<double>(ar);
Since you are not allowed to change MyArray, one option is to use an adapter class.
template <typename T>
class ConstMyArrayView {
public:
// Not an explicit constructor!
ConstMyArrayView(const MyArray<T>& a) : a_(a) {}
const T& operator[](size_t i) const { return a_[i]; }
private:
const MyArray<T>& a_;
};
template<typename T>
void foo(const ConstMyArrayView<T>& x);
MyArray<T> x;
foo(x);
But in the end, if you can change MyArray to match the const-correctness you want, or switch to a class that does, that'll be the better option.
Here's an ugly but effective way to have a function use one type, but also get the compiler to check that the same code would compile if it used a different type instead:
template <typename From, typename To>
struct xfer_refs_cv
{
using type = To;
};
template <typename From, typename To>
struct xfer_refs_cv<const From, To>
{
using type = const typename xfer_refs_cv<From, To>::type;
};
template <typename From, typename To>
struct xfer_refs_cv<volatile From, To>
{
using type = volatile typename xfer_refs_cv<From, To>::type;
};
template <typename From, typename To>
struct xfer_refs_cv<From&, To>
{
using type = typename xfer_refs_cv<From, To>::type&;
};
template <typename From, typename To>
struct xfer_refs_cv<From&&, To>
{
using type = typename xfer_refs_cv<From, To>::type&&;
};
template <typename CheckType, typename Func, typename CallType>
constexpr decltype(auto) check_and_call(Func&& f, CallType&& call_arg)
noexcept(noexcept(std::forward<Func>(f)(std::forward<CallType>(call_arg))))
{
(void) decltype(std::declval<Func&&>()
(std::declval<typename xfer_refs_cv<CallType&&, CheckType>::type>()), 0){};
return std::forward<Func>(f)(std::forward<CallType>(call_arg));
}
template<typename T>
void foo(const MyArray<T>& x)
{
check_and_call<MyArray<const T>>(
[](auto&& x) {
// Function implementation here.
}, x);
}
When compiled, the following code causes this error:
'Container::Wrapper::Wrapper(S)': member function already
defined or declared
Does the compiler think S&& in the constructor of Wrapper is a forwarding reference?
template<typename T>
struct Container
{
template<class S>
struct Wrapper {
S obj;
Wrapper(S&& obj) : obj(std::forward<S>(obj)){}
Wrapper(const S& obj) : obj(obj){}
};
template<class S>
void push_back(S&& obj) {
void *storage = malloc(sizeof(Wrapper<S>));
new (storage) Wrapper<S>(std::forward<S>(obj));
}
};
struct Foobar{};
int main()
{
Container<Foobar> cont;
Foobar foobar;
cont.push_back(foobar);
return 0;
}
Looking at an example from here, I don't understand how what I'm doing is any different:
template <class T, class Allocator = allocator<T> >
class vector {
public:
...
void push_back(T&& x); // fully specified parameter type ⇒ no type deduction;
... // && ≡ rvalue reference
};
Edit:
The solution to this issue was to modify push_back to remove the reference from the type being used to instantiate the Wrapper:
template<class S>
void push_back(S&& obj) {
typedef std::remove_reference<S>::type U;
void *storage = malloc(sizeof(Wrapper<U>));
new (storage) Wrapper<U>(std::forward<S>(obj));
}
There are no universal references involved it the implementation of Wrapper. The only universal reference in your code is Container<>::push_back's parameter.
When you call cont.push_back(foobar);, parameter S of push_back is deduced as Foobar &.
Later you attempt to instantiate your Wrapper<S> with S == Foobar &. Reference collapsing rules dictate that in Wrapper's constructor parameter declarations S && turns into Foobar & and const S & also turns into Foobar &.
This means that you end up with two supposedly "overloaded" Wrapper::Wrapper constructors, which have identical signatures. This is the reason for the error message you observe.
If you attempt to instantiate std::vector with a template argument of lvalue reference type, you will run into exactly the same problem for its push_back overloads. However, with such an attempt compilation will typically fail miserably for other reasons, well before it gets to push_back overloads.
A more distilled example would look as follows
template <typename T> struct MyVector {
void foo(T &&) {}
void foo(const T &) {}
};
int main() {
MyVector<int &> v;
}
and will produce the same error.
The fairly non-obvious part here is that const T & with T = U & actually becomes U & and not const U &. But that's indeed the case.
Note: everything that follows uses the Concepts TS implementation in GCC 6.1
Let's say I have a concept Surface, like the following:
template <typename T>
concept bool Surface() {
return requires(T& t, point2f p, float radius) {
{ t.move_to(p) };
{ t.line_to(p) };
{ t.arc(p, radius) };
// etc...
};
}
Now I want to define another concept, Drawable, which matches any type with a member function:
template <typename S>
requires Surface<S>()
void draw(S& surface) const;
i.e.
struct triangle {
void draw(Surface& surface) const;
};
static_assert(Drawable<triangle>(), ""); // Should pass
That is, a Drawable is something which has a templated const member function draw() taking an lvalue reference to something which satisfies the Surface requirements. This is reasonably easy to specify in words, but I can't quite work out how to do it in C++ with the Concepts TS. The "obvious" syntax doesn't work:
template <typename T>
concept bool Drawable() {
return requires(const T& t, Surface& surface) {
{ t.draw(surface) } -> void;
};
}
error: 'auto' parameter not permitted in this context
Adding a second template parameter allows the concept definition to compile, but:
template <typename T, Surface S>
concept bool Drawable() {
return requires(const T& t, S& s) {
{ t.draw(s) };
};
}
static_assert(Drawable<triangle>(), "");
template argument deduction/substitution failed:
couldn't deduce template parameter 'S'
now we can only check whether a particular <Drawable, Surface> pair matches the Drawable concept, which isn't quite right. (A type D either has the required member function or it does not: that doesn't depend on which particular Surface we check.)
I'm sure it's possible to do what I'm after, but I can't work out the syntax and there aren't too many examples online yet. Does anybody know how to write a concept definition which requires type to have a constrained template member function?
What you're looking for is for a way for the compiler to synthesize an archetype of Surface. That is, some private, anonymous type that minimally satisfies the Surface concept. As minimally as possible. Concepts TS doesn't currently allow for a mechanism for automatically synthesizing archetypes, so we're left with doing it manually. It's quite a complicated process, since it's very easy to come up with archetype candidates that have way more functionality that the concept specifies.
In this case, we can come up with something like:
namespace archetypes {
// don't use this in real code!
struct SurfaceModel {
// none of the special members
SurfaceModel() = delete;
SurfaceModel(SurfaceModel const& ) = delete;
SurfaceModel(SurfaceModel&& ) = delete;
~SurfaceModel() = delete;
void operator=(SurfaceModel const& ) = delete;
void operator=(SurfaceModel&& ) = delete;
// here's the actual concept
void move_to(point2f );
void line_to(point2f );
void arc(point2f, float);
// etc.
};
static_assert(Surface<SurfaceModel>());
}
And then:
template <typename T>
concept bool Drawable() {
return requires(const T& t, archetypes::SurfaceModel& surface) {
{ t.draw(surface) } -> void;
};
}
These are valid concepts, that probably work. Note that there's a lot of room for even more refinement on the SurfaceModel archetype. I have a specific function void move_to(point2f ), but the concept just requires that it's callable with an lvalue of type point2f. There's no requirement that move_to() and line_to() both take an argument of type point2f, they could both take complete different things:
struct SurfaceModel {
// ...
struct X { X(point2f ); };
struct Y { Y(point2f ); };
void move_to(X );
void line_to(Y );
// ...
};
This kind of paranoia makes for a better archetype, and serves to illustrate how complex this problem could be.
Recently I often encountered the problem, that I had to write a function which takes an input as a const reference. But at some point this function (usually a constructor) calls another function which could use the input as a move reference. For that reason I usually created a copy of the function to allow const reference and move reference, i.e.
#include <iostream>
class A {};
void foo(const A& a) { std::cout << "Reference" << std::endl; }
void foo( A&& a) { std::cout << "Move" << std::endl; }
void bar(const A& a) {
//Other things, which treat "a" as a const reference
foo(std::move(a));
}
void bar(A&& a) {
//Other things, which treat "a" as a const reference
foo(std::move(a));
}
int main() {
A a;
bar(a);
bar(A());
}
However it is obviously pretty ugly to copy bar two times with the only difference being the signature and the std::move. One alternative I know would be to make bar a tempalte function and to use std::forward, but I don't want to do that because it would allow any parameter type to be passed to bar (especially since in my real application bar is an implicit constructor).
So my question is: Is there any other way to forward a move reference through a function without writing it twice?
If you want to accept both rvalues and lvalues in a single function, retaining the possibility to restore the value category of its argument, you can use a forwarding-reference. You can easily restrict the type of arguments passed in utilizing the expression SFINAE technique, which in your case will verify if the call foo(std::forward<T>(a)) is well-formed. If not, that function will be excluded from the set of viable functions during the overload resolution:
Option #1
Hide the expression SFINAE in a trailing return type:
template <typename T>
auto bar(T&& a)
-> decltype(void(foo(std::forward<T>(a))))
{
//Other things, which treat "a" as a const reference
foo(std::forward<T>(a));
}
DEMO 1
Option #2
Hide the expression SFINAE in a template parameters list:
template <typename T,
typename = decltype(foo(std::forward<T>(std::declval<T&>())))>
void bar(T&& a)
{
//Other things, which treat "a" as a const reference
foo(std::forward<T>(a));
}
DEMO 2
The latter approach is especially useful for constructors (which don't specify a return type):
struct Bar
{
template <typename T,
typename = decltype(foo(std::forward<T>(std::declval<T&>())))>
Bar(T&& a)
{
foo(std::forward<T>(a));
}
};
DEMO 3
Perfect forwarding with SFINAE works and keeps the overload set unpolluted. You first have to decide what exactly should be checked, e.g. the type set this template should be invoked for or expressions that should be valid.
Here, both suffice - this code checks the type:
// Helper template:
template <typename T, typename U, typename R=void>
using enable_if_compatible = typename std::enable_if<std::is_same<U,
typename std::remove_cv<
typename std::remove_reference<T>::type>::type>::value, R>::type;
// Possible usage:
template <typename T>
enable_if_compatible<T, A> bar(T&& a)
{
//Other things, which treat "a" as a const reference
foo(std::forward<T>(a));
}
Demo.
The following one depends on the validity of the call to foo and should be more flexible.
template <typename T>
auto bar(T&& a) -> decltype(void(foo(std::forward<T>(a))))
{
//Other things, which treat "a" as a const reference
foo(std::forward<T>(a));
}
Demo.
One option is to add a templated version of bar that takes a pointer to foo and contains all of the common code that is currently in existing implementations of bar.
template<class T>
void bar(T&& a, void (*f)(T&&a))
{
//Other things, which treat "a" as a const reference
f(std::move(a));
}
void bar(const A& a)
{
bar<const A&>(a, foo);
}
void bar(A&& a)
{
bar(std::move(a), foo);
}