When does && mean 'forwarding reference'? - c++

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.

Related

Pass object with derived template to function that accepts object with base template

How do I pass an object with a derived template instantiation to a method accepting those objects with a base template instantiation?
It seems possible, as std::shared_ptr or std::pair seem capable to do it.
E.g.
#pragma once
#include <iostream>
#include <memory>
struct Base {
virtual void print() = 0;
};
struct Derived : public Base {
void print() {
std::cout << "Got it!" << std::endl;
}
};
void printBase(const std::shared_ptr<Base> &ptr){
ptr->print();
}
void printBase(const std::pair<Base&, Base&> &pr){
pr.first.print();
}
template <typename T>
struct Wrap {
T& t;
};
void printBase(const Wrap<Base> &wrap) {
wrap.t.print();
}
int main() {
Derived d;
std::shared_ptr<Derived> ptr = std::make_shared<Derived>(d);
printBase(ptr); // works
std::pair<Derived&, Derived&> pr = {d, d};
printBase(pr); // works
Wrap<Derived> w = Wrap<Derived>{d};
// printBase(w); // gives compile error
}
You will need to explicitly add a conversion constructor and/or assignment operator to your Wrapped type to be able to convert from different types.
This is how both std::shared_ptr and std::pair do this internally; shared_ptr<T> can be constructed from shared_ptr<U> types (with SFINAE restrictions that U* is convertible to T*), and pair<T,U> can be constructed from pair<T2,U2> types (with SFINAE restrictions that T2 is convertible to T and U2 is convertible to U).
Doing this can be as simple as adding a new constructor:
template <typename T>
struct Wrap
{
Wrap(T& ref)
: t{ref}
{
}
template <typename U, typename = std::enable_if_t<std::is_convertible_v<U&, T&>>>
Wrap(const Wrap<U>& other)
: t{other.t}
{
}
T& t;
};
The above example uses is_convertible as a condition for enable_if so the constructor is only visible when references of U can be converted to references of T. This will constrain it such that U must be hierarchically related to T (since references aren't convertible otherwise) -- which would allow a Wrapped<Derived> to be converted to Wrapped<Base>, but not vice-versa.
Edit: As mentioned in the comments, it's worth noting that, unlike types that are part of a hierarchy -- where a reference to Derived can be passed as a reference to Base, types that wrap hierarchies will not be able to pass a reference to a Template<Derived> as a reference to a Template<Base>.
The examples with a std::shared_ptr<Derived> being passed to a const std::shared_ptr<Base>& only actually work due to const-lifetime extension in C++. This doesn't actually pass it as a reference -- but instead materializes a temporary object of std::shared_ptr<Base> which gets passed to the reference. It's effectively the same as passing-by-value.
This also means that you cannot have a Template<Derived> be passed to a non-const Template<Base> reference, since lifetime extension only occurs for const references.
Edit: As discussed in the comments:
Making the constructor a copy conversion is not required; it could easily be an R-value constructor instead. However, if you are doing cleanup on destruction of the wrapped type, then you will need to somehow flag that the moved-from object does not need to be cleaned up. The easiest way is using pointers, where you rebind to nullptr after moving:
template <typename T>
struct Wrap
{
Wrap(T& ref)
: t{std::addressof(ref)}
{
}
Wrap(Wrap&& other)
: t{other.t}
{
other.t = nullptr;
}
Wrap(const Wrap&) = delete;
template <typename U, typename = std::enable_if_t<std::is_convertible_v<U&, T&>>>
Wrap(Wrap<U>&& other)
: t{other.t}
{
other.t = nullptr;
}
~Wrap() {
if (t != nullptr) {
cleanup(*t); // some cleanup code
}
}
T* t;
};
If pointers aren't possible for your desired API, then you may need to use a bool needs_cleanup that gets set appropriately during moves, since references cannot be rebound.
Note: if the data is private and not public as in this example, you may need to have a friend declaration of:
template <typename> friend class Wrap;
So that a Wrap<T> may access the private data members of a Wrap<U>.
Change printBase function to the following one:
template <typename T, typename = std::enable_if<std::is_base_of_v<Base, T>>>
void printBase(const Wrap<T> &wrap){
wrap.t.print();
}

Deducing the template parameter of a templated class argument: const issue

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);
}

Template parameter can't be deduced on implicitly constructed argument

I would like to have the following code in c++17:
#include <iostream>
#include <string>
#include <type_traits>
#include <functional>
class Foo;
template<class T>
class Bar {
public:
std::function<T(Foo&)> m_fn;
template<class Fn>
Bar(Fn fn) : m_fn(fn) {};
T thing(Foo &foo) const {
return m_fn(foo);
}
};
template<class Fn>
Bar(Fn) -> Bar<decltype(std::invoke(std::declval<Fn>(),
std::declval<Foo&>()))>;
class Foo {
public:
Foo() {};
template<class T>
std::vector<T> do_thing(const Bar<T> &b) {
std::vector<T> r;
r.push_back(b.thing(*this));
return r;
}
};
std::string test(Foo &) {
return "hello";
}
int main() {
Foo foo = Foo();
// works
std::vector<std::string> s = foo.do_thing(Bar{test});
// cant deduce T parameter to do_thing
std::vector<std::string> s = foo.do_thing({test});
}
But compiling this gives me "couldn't deduce template parameter ‘T’" on the call to do_thing.
Having do_thing(Bar{test}) fixes this and works fine but equates to some ugly code in the real code equivalent. I would like to have do_thing({test}) or do_thing(test) implicitly construct a Bar and pass that as the argument if possible.
I also don't want to forward declare a variable to pass into do_thing either
Is there some way to guide the inference of template argument T so that the call to do_thing can stay clean?
Edit:
Sorry for the late edit, but the arguments to the Bar constructor are over simplified in the example I included. In reality, there is an extra parameter std::optional<std::string> desc = std::nullopt and that might change in the future (although unlikely). So constructing the Bar inside do_thing would be a bit hard to maintain...
would like to have do_thing({test}) or do_thing(test) implicitly construct a Bar and pass that as the argument if possible.
Unfortunately, when you call do_thing({test}) or do_thing(test), test (or {test}) isn't a Bar<T> object. So the compiler can't deduce the T type and can't construct a Bar<T> object.
A sort of chicken-and-egg problem.
The best I can imagine is to add, in Foo, a do_test() method as follows
template<typename T>
auto do_thing (T const & t)
{ return do_thing(Bar{t}); }
This way you can call (without graphs)
std::vector<std::string> s = foo.do_thing(test);
You get the same result as
std::vector<std::string> s = foo.do_thing(Bar{test});
-- EDIT --
The OP ask
is there any way of preserving the {test} brace syntax? maybe with initializer_list or something?
Yes... with std::initializer_list
template<typename T>
auto do_thing (std::initializer_list<T> const & l)
{ return do_thing(Bar{*(l.begin())}); }
but, this way, you accept also
std::vector<std::string> s = foo.do_thing(Bar{test1, test2, test3});
using only test1
Maybe a little better... another way can be through a C-style array
template <typename T>
auto do_thing (T const (&arr)[1])
{ return do_thing(arr[0]); }
This way you accept only an element.
This happens because {} is not an expression and can only be used in limited ways while doing argument deduction, the parameter must have specific forms in order to succeed.
The allowed parameters types that can be used to deduce template parameters when {} is involved are better expanded in [temp.deduct.call]/1, two of the examples extracted from the cited part of the standard are:
template<class T> void f(std::initializer_list<T>);
f({1,2,3}); // T deduced to int
template<class T, int N> void h(T const(&)[N]);
h({1,2,3}); // T deduced to int
In your example the deduction guide is not used to deduce the T for {test} for the same as above.
foo.do_thing(Bar{test});
is your direct option without using additional functions.

Cannot bind lvalue to A<Cv2>&&

I thought universal reference (T&&) is supposed to take any kind of reference. But the following doesn't work.
I run into this problem when I try to be const-correct in a library that I am writing. I am new to C++ and haven't seen something like this before.
test.cpp:
enum Cv_qualifier {
constant,
non_const
};
template <Cv_qualifier Cv> class A;
template<>
class A<Cv_qualifier::constant> {
public:
template<Cv_qualifier Cv2>
void t(const A<Cv2>&& out) {}
};
template <>
class A<Cv_qualifier::non_const> {
public:
template<Cv_qualifier Cv2>
void t(const A<Cv2>&& out) {}
};
int main()
{
A<Cv_qualifier::non_const> a;
A<Cv_qualifier::constant> b;
a.t(b);
}
Error (compiled with g++ test.cpp -std=c++11):
test.cpp: In function ‘int main()’:
test.cpp:24:10: error: cannot bind ‘A<(Cv_qualifier)0u>’ lvalue to ‘const A<(Cv_qualifier)0u>&&’
a.t(b);
^
test.cpp:17:10: note: initializing argument 1 of ‘void A<(Cv_qualifier)1u>::t(const A<Cv2>&&) [with Cv_qualifier Cv2 = (Cv_qualifier)0u]’
void t(const A<Cv2>&& out) {}
^
By the way, in the actual program, the class A does not own any actual data, and contain references to another class that actually hold the data. I hope this means I am not constantly create indirection/copy data when I allow the member function t of class A to accept temporary objects.
Universal reference, or forwarding reference, only happen because of reference collapsing. It work that way:
T&& & -> T&
T& && -> T&
T&& && -> T&&
That way, when you receive T&& in a template function, the rvalue reference can collapse to other types of reference depending of the type of T. In any other cases, when the collapsing don't happen, SomeType&& will stay SomeType&& and will be an rvalue reference.
With that said, if you want your function to support forwarding, you can do that:
template <Cv_qualifier Cv> struct A;
template<>
struct A<Cv_qualifier::constant> {
template<typename T>
void t(T&& out) {}
};
template <>
struct A<Cv_qualifier::non_const> {
template<typename T>
void t(T&& out) {}
};
Indeed, now the collapsing happen. If you want to extract the Cv_qualifier value from T, you can make yourself a type trait that do that:
template<typename>
struct CvValue;
template<Cv_qualifier cv>
struct CvValue<A<cv>> {
constexpr static Cv_qualifier value = cv;
};
Then, inside your function t, you can do that:
// v----- This is a good practice to apply a constraint
template<typename T, std::void_t<decltype(CvValue<std::decay_t<T>>::value)>* = 0>
auto t(T&& out) {
constexpr auto cv = CvValue<std::decay_t<T>>::value;
// do whatever you want with cv
}
If you can't use C++17's std::void_t, you can implement it like that:
template<typename...>
using void_t = void;
However, if you only want to test if T is an A<...>, use this:
template<typename>
struct is_A : std::false_type {};
template<Cv_qualifier cv>
struct is_A<A<cv>> : std::true_type {};
Don't forget, to use it with std::decay_t:
template<typename T, std::enable_if_t<std::is_A<std::decay_t<T>>::value>* = 0>
void t(T&& out) {}

Forward a move reference through a function, without writing the function twice

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);
}