Generating the necessary ref-qualified overloads for a member function - c++

I have this class:
template<typename T, size_t N>
class Array {
private:
T array[N];
public:
template <typename... InitValues>
constexpr Array(InitValues... init_values)
: array{ init_values... } {}
[[nodiscard]]
consteval int len() const noexcept { return sizeof(array) / sizeof(T); }
}
I would like to know, for such a simple member function, when I should provide the necessary ref-qualified overloads.
With the actual code, I can compile and run the following code:
constexpr collections::Array a = collections::Array<long, 5>{1L, 2L, 3L};
SECTION("length of the array") {
REQUIRE( a.len() == 5 );
REQUIRE( collections::Array<int, 1>{1}.len() == 1 );
}
1- Why I can compile the second REQUIRE that contains the call with the rvalue?
Now I am gonna change the len() member function to this:
[[nodiscard]]
consteval int len() const& noexcept { return sizeof(array) / sizeof(T); }
2- Why I can compile both with the const&? I suppose that they are two are different ref-qualified usages. I assume that I can make the call with the first one, which is an lvalue, but can't understand why I can compile the second having defined the len() method as const&.
Last change:
[[nodiscard]]
consteval int len() const&& noexcept { return sizeof(array) / sizeof(T); }
And finally, I got a compiler error on a.get<I>().
'this' argument to member function 'len' is an lvalue, but function has rvalue ref-qualifier
REQUIRE( a.len() == 5 );
that works perfect if I comment that line of code and I just run:
REQUIRE( collections::Array<int, 1>{1}.len() == 1 );
and also I could use std::move(a) to perform the cast of a to an rvalue reference and make the code compile. But I don't want to do that.
What is the correct way of code those examples in terms of ref-qualified overloads?
Don't forget about the questions on the examples above
EDIT:
I will add another member function that could potentially do different things based on the ref-qualified implementation (or that what I am suppose that could happen):
template <size_t I>
requires concepts::AccessInBounds<I, N>
constexpr T get() const noexcept {
return array[I];
}
template <size_t I>
requires concepts::AccessInBounds<I, N>
constexpr T& get() const& noexcept {
return array[I];
}

To question 1: why not? The rule is the same as for lvalues: you can call const member functions regardless of the constness of the object.
To question 2: Because it is meant to be identical to having a const& function parameter: the function can be called with any lvalue or rvalue. It exists primarily to allow you to distinguish between lvalue and rvalue overloads:
class Array {
// These two declarations would be ambiguous for Array rvalues
// int len() const;
// int len() &&;
// These are not: your test expressions will use different overloads
int len() const&;
int len() &&;
};
The two functions in your edit are also ambiguous, for both lvalues and rvalues. A motivating example would be more along these lines: suppose my class provides functionality to some resource that could be expensive to copy, but is cheaper to move, say a std::vector.
template<class T>
class VectorView {
std::vector<T> vector;
public:
// ...
constexpr std::vector<T> const& base() const noexcept { return vector; }
};
Now there is no way for a user of this class to transfer ownership of the vector data back from a view object, even if that would be useful when calling the base() function on an rvalue. Because it is in the spirit of C++ to avoid paying for things you do not need, you could allow this by adding an rvalue-qualified overload that instead returns an rvalue reference using std::move.
So the answer to whether you need this kind of overload is it depends, which is unfortunately also in the spirit of C++. If you were implementing something like my example class for the standard library, then you certainly would, because it is based on std::ranges::owning_view. As you can see on that page, it covers all four possible base()s. If you were instead only using a reference to a source range, it would be unexpected and inappropriate to move from that object, so the related ref_view only has a const base() function like the one I wrote.
Edit As for move semantics, the difference between something like an array and a vector is that Array<T,N> is based on T[N], while std::vector<T> is based on T*. Moving the array requires N move operations (linear time complexity), and whether a move is an improvement over a copy depends on T. Also, it needs memory space for 2N elements. On the other hand, a vector only ever needs three pointers to do its job, so it can be moved in constant time, while copying still takes linear time.
This potential gain is the rationale for move semantics and rvalue references in a nutshell. The ability to also have &&-qualified member functions completes this language feature, but is not as significant as move constructors and assignment functions. I also found the answers to this question useful, as they give some more examples of ref-qualified overloads.

Related

How to create a type wrapper with reference-like semantics?

I want an object which can wrap a value symantic type and pretend that it is a reference.
Something like this:
int a = 20;
std::list<Wrap<int>> l1;
l1.push_back(a);
std::list<Wrap<int>> l2;
l2.push_back(a);
l2.front() = 10;
cout << l1.front() << endl; // output should be 10
While writing this question it occured to me that a shared_ptr might be what I want. However I am not sure if a pointer symantic object is what I am looking for. Perhaps there is no alternative in the standard library?
std::shared_ptr<int> a = std::make_shared(10);
std::list<std::shared_ptr<int>> l1;
l1.push_back(a);
std::list<std::shared_ptr<int>> l2;
l2.push_back(a);
*(l2.front()) = 20; // not exactly what I wanted to write
cout << *l1.front() << endl; // prints 10
I found std::reference_wrapper but this appears to be the opposite of what I want. It appears to permit a reference type to be used like a value type which is the reverse of what I wanted.
Any thoughts?
Proxy Object
As far as I know, there isn’t such a wrapper in the Standard Library. Fortunately though, you can implement one yourself rather easily.
How to start? Well, we’d like our Wrap<T> to be possibly indistinguishable from a T&. To achieve this, we can implement it as a proxy object. So, we’ll be making a class template storing a T& ref and implement operations one by one:
template<typename T>
struct Wrap
{
private:
T& ref;
public:
// ???
};
Construction
Let’s start with constructors. If we want Wrap<T> to behave like a T& it should be constructible from the same things that T& is constructible. Well then, what is a reference x constructible from?
A non-const lvalue reference like T& x = ...; needs to be constructed from an lvalue of type T. This means that it can be constructed from:
an object of type T
an object of a type derived from T
a different lvalue reference to T
an object convertible to T&
a function call returning T&
A const lvalue reference like const T& x = ...; can also be constructed from a braced-initializer-list or materialized from a temporary, however if we wanted to implement that, then we’d need to actually store a T inside of our class. As such, let’s focus on a non-const reference.
First, let’s implement a constructor from T& which will cover all of the cases shown above:
constexpr Wrap(T& t) noexcept :
ref{t}
{
}
The constructor is constexpr and noexcept because it can be. It is not explicit because we want to be able to use Wrap as transparently as possible.
We need to remember though, that a reference needs to be initializable from a different reference. Because we want our type to behave just like a builtin reference, we need to be able to
initialize a Wrap from a T& (already implemented above)
initialize a Wrap from a different Wrap
initialize a T& from a Wrap
To meet these criteria, we’ll need to implement a conversion operator to T&. If Wrap<T> will be implicitly convertible to T&, then assigning it to a builtin reference will work.
Actually, this:
int x;
Wrap w = x;
Wrap w2 = w;
will also work (ie. w2 will be Wrap<int> rather than a Wrap<Wrap<int>>) because the conversion operator takes precedence.
The implementation of the conversion operator looks like this:
constexpr operator T&() const noexcept
{
return ref;
}
Note that it is constexpr, noexcept and const, but not explicit (just like the constructor).
Miscelaneous operations
Now that we can construct our custom reference wrapper, we’d also like to be able to use it. The natural question is “what can you do with a reference”? Well, builtin references aren’t objects but merely aliases to existing objects. This means that taking the address of the reference actually returns the address of the referent or that you can access members through a reference.
All these things could be implemented, for example by overloading the arrow operator operator-> or the address-of operator operator&. For simplicity though, I will omit implementing these operations and focus only on the most important one: assignment.
Assignment
Firstly, we’d like to be able to assign a Wrap<T> to a T& or a T or basically anything that can be assigned a T. Fortunately, we already got that covered by having implemented the conversion operator.
Now we only need to implement assignment to Wrap<T>. We could be tempted to just write the operator like this:
constexpr T& operator=(const T& t)
{
ref = t;
return ref;
}
constexpr T& operator=(T&& t) noexcept
{
ref = std::move(t);
return ref;
}
Seems fine, right? We have a copy assignment operator and a noexcept move assignment operator. We return a reference as per custom.
Well, the problem is that this implementation is incomplete. The thing is that we don’t check
is T copy-assignable
if it is, then is it nothrow-copy-assignable
is T move-assignable
if it is, then is it nothrow-move-assignable
what is the return type of T’s assignment operator (it could be the customary T&, but it could also theoretically be anything else, like void)
does T have any other assignment operators
This is a lot of cases to cover. Fortunately, we can solve this all by making our assignment operator a template.
Let’s say that the operator will take an object of arbitrary type U as its argument. This will cover both the copy and move assignment operators and any other potential assignment operators. Then, let’s say that the return type of the function will be auto, to let the compiler deduce it. This gives us the following implementation:
template <typename U>
constexpr auto operator=(U u)
{
return (ref = u);
}
Unfortunately though, this implementation is still not complete.
We don’t know if the assignment is noexcept
We don’t distinguish copy assignment and move assignemnt and could potentially be making unnecessary copies.
Is the return type (auto) really correct?
To solve the first issue we can use a conditional noexcept. To check if the assignment operator is noexcept we can either use the type trait std::is_nothrow_assignable_v or the noexcept operator. I think that using the noexcept operator is both shorter and less error-prone, so let’s use that:
template <typename U>
constexpr auto operator=(U u) noexcept(noexcept(ref = u))
{
return (ref = u);
}
To solve the issue of distinguishing copies and moves, instead of taking a U u, we can take a forwarding reference U&& u, to let the compiler deal with all of this. We also need to remember about using std::forward:
template <typename U>
constexpr auto operator=(U&& u) noexcept(noexcept(ref = std::forward<U>(u)))
{
return (ref = std::forward<U>(u));
}
There is a bit of code duplication, but, unfortunately, it is inevitable, unless we’d use std::is_nothrow_assignable_v instead.
Finally, is the return type correct? Well, no. Because C++ is C++, parentheses around the returned value actually change its type (ie. return(x); is different from return x;). To return the correct type, we’ll actually also need to apply perfect forwarding to the returned type as well. We can do this by either using a trailing return type or a decltype(auto) return type. I will use decltype(auto) as it’s shorter and avoids duplicating the function body yet again:
template <typename U>
constexpr decltype(auto) operator=(U&& u) noexcept(noexcept(ref = std::forward<U>(u)))
{
return ref = std::forward<U>(u);
}
Conclusion
Now, finally, we have a complete implementation. To sum things up, here it is all together (godbolt):
template<typename T>
struct Wrap
{
private:
T& ref;
public:
constexpr Wrap(T& t) noexcept :
ref{t}
{
}
constexpr operator T&() const noexcept
{
return ref;
}
template <typename U>
constexpr decltype(auto) operator=(U&& u) noexcept(noexcept(ref = std::forward<U>(u)))
{
return ref = std::forward<U>(u);
}
};
That was quite a bit of C++ type theory to get through to write these 21 lines. Oh, by the way, did I mention value categories...

How do correctly use a callable passed through forwarding reference?

I'm used to pass lambda functions (and other callables) to template functions -- and use them -- as follows
template <typename F>
auto foo (F && f)
{
// ...
auto x = std::forward<F>(f)(/* some arguments */);
// ...
}
I mean: I'm used to pass them through a forwarding reference and call them passing through std::forward.
Another Stack Overflow user argue (see comments to this answer) that this, calling the functional two or more time, it's dangerous because it's semantically invalid and potentially dangerous (and maybe also Undefined Behavior) when the function is called with a r-value reference.
I've partially misunderstand what he means (my fault) but the remaining doubt is if the following bar() function (with an indubitable multiple std::forward over the same object) it's correct code or it's (maybe only potentially) dangerous.
template <typename F>
auto bar (F && f)
{
using A = typename decltype(std::function{std::forward<F>(f)})::result_type;
std::vector<A> vect;
for ( auto i { 0u }; i < 10u ; ++i )
vect.push_back(std::forward<F>(f)());
return vect;
}
Forward is just a conditional move.
Therefore, to forward the same thing multiple times is, generally speaking, as dangerous as moving from it multiple times.
Unevaluated forwards don't move anything, so those don't count.
Routing through std::function adds a wrinkle: that deduction only works on function pointers and on function objects with a single function call operator that is not && qualified. For these, rvalue and lvalue invocation are always equivalent if both compiles.
I'd say the general rule applies in this case. You're not supposed to do anything with a variable after it was moved/forwarded from, except maybe assigning to it.
Thus...
How do correctly use a callable passed through forwarding reference?
Only forward if you're sure it won't be called again (i.e. on last call, if at all).
If it's never called more than once, there is no reason to not forward.
As for why your snippet could be dangerous, consider following functor:
template <typename T>
struct foo
{
T value;
const T &operator()() const & {return value;}
T &&operator()() && {return std::move(value);}
};
As an optimization, operator() when called on an rvalue allows caller to move from value.
Now, your template wouldn't compile if given this functor (because, as T.C. said, std::function wouldn't be able to determine return type in this case).
But if we changed it a bit:
template <typename A, typename F>
auto bar (F && f)
{
std::vector<A> vect;
for ( auto i { 0u }; i < 10u ; ++i )
vect.push_back(std::forward<F>(f)());
return vect;
}
then it would break spectacularly when given this functor.
If you're either going to just forward the callable to another place or simply call the callable exactly once, I would argue that using std::forward is the correct thing to do in general. As explained here, this will sort of preserve the value category of the callable and allow the "correct" version of a potentially overloaded function call operator to be called.
The problem in the original thread was that the callable was being called in a loop, thus potentially invoked more than once. The concrete example from the other thread was
template <typename F>
auto map(F&& f) const
{
using output_element_type = decltype(f(std::declval<T>()));
auto sequence = std::make_unique<Sequence<output_element_type>>();
for (const T& element : *this)
sequence->push(f(element));
return sequence;
}
Here, I believe that calling std::forward<F>(f)(element) instead of f(element), i.e.,
template <typename F>
auto map(F&& f) const
{
using output_element_type = decltype(std::forward<F>(f)(std::declval<T>()));
auto sequence = std::make_unique<Sequence<output_element_type>>();
for (const T& element : *this)
sequence->push(std::forward<F>(f)(element));
return sequence;
}
would be potentially problematic. As far as my understanding goes, the defining characteristic of an rvalue is that it cannot explicitly be referred to. In particular, there is naturally no way for the same prvalue to be used in an expression more than once (at least I can't think of one). Furthermore, as far as my understanding goes, if you're using std::move or std::forward or whatever other way to obtain an xvalue, even on the same original object, the result will be a new xvalue every time. Thus, there also cannot possibly be a way to refer to the same xvalue more than once. Since the same rvalue cannot be used more than once, I would argue (see also comments underneath this answer) that it would generally be a valid thing for an overloaded function call operator to do something that can only be done once in case the call happens on an rvalue, for example:
class MyFancyCallable
{
public:
void operator () & { /* do some stuff */ }
void operator () && { /* do some stuff in a special way that can only be done once */ }
};
The implementation of MyFancyCallable may assume that a call that would pick the &&-qualified version cannot possibly happen more than once (on the given object). Thus, I would consider forwarding the same callable into more than one call to be semantically broken.
Of course, technically, there is no universal definition of what it actually means to forward or move an object. In the end, it's really up to the implementation of the particular types involved to assign meaning there. Thus, you may simply specify as part of your interface that potential callables passed to your algorithm must be able to deal with being called multiple times on an rvalue that refers to the same object. However, doing so pretty much goes against all the conventions for how the rvalue reference mechanism is generally used in C++, and I don't really see what there possibly would be to be gained from doing this…

Forwarding reference vs const lvalue reference in template code

I've recently been looking into forwarding references in C++ and below is a quick summary of my current understanding of the concept.
Let's say I have a template function footaking a forwarding reference to a single argument of type T.
template<typename T>
void foo(T&& arg);
If I call this function with an lvalue then T will be deduced as T& making the arg parameter be of type T& due to the reference collapsing rules T& && -> T&.
If this function gets called with an unnamed temporary, such as the result of a function call, then Twill be deduced as T making the arg parameter be of type T&&.
Inside foo however, arg is a named parameter so I will need to use std::forward if I want to pass the parameter along to other functions and still maintain its value category.
template<typename T>
void foo(T&& arg)
{
bar(std::forward<T>(arg));
}
As far as I understand the cv-qualifiers are unaffected by this forwarding. This means that if I call foo with a named const variable then T will be deduced as const T& and hence the type of arg will also be const T& due to the reference collapsing rules. For const rvalues T will be deduced as const T and hence arg will be of type const T&&.
This also means that if I modify the value of arg inside foo I will get a compile time error if I did infact pass a const variable to it.
Now onto my question.
Assume I am writing a container class and want to provide a method for inserting objects into my container.
template<typename T>
class Container
{
public:
void insert(T&& obj) { storage[size++] = std::forward<T>(obj); }
private:
T *storage;
std::size_t size;
/* ... */
};
By making the insert member function take a forwarding reference to obj I can use std::forward to take advantage of the move assignment operator of the stored type T if insert was infact passed a temporary object.
Previously, when I didn't know anything about forwarding references I would have written this member function taking a const lvalue reference:
void insert(const T& obj).
The downside of this is that this code does not take advantage of the (presumably more efficient) move assignment operator if insert was passed a temporary object.
Assuming I haven't missed anything.
Is there any reason to provide two overloads for the insert function? One taking a const lvalue reference and one taking a forwarding reference.
void insert(const T& obj);
void insert(T&& obj);
The reason I'm asking is that the reference documentation for std::vectorstates that the push_back method comes in two overloads.
void push_back (const value_type& val);
void push_back (value_type&& val);
Why is the first version (taking a const value_type&) needed?
You have to be careful about function templates, versus non-template methods of class templates. Your member insert is not itself a template. It's a method of a template class.
Container<int> c;
c.insert(...);
We can pretty easily see that T is not deduced on the second line, because it's already fixed to int on the first line, because T is a template parameter of the class, not the method.
Non-template methods of class templates, only differ from regular methods in one way, once the class has been instantiated: they aren't instantiated unless they are actually called. This is useful because it allows a template class to work with types, for which only some of the methods make sense (STL containers are full of examples like this).
The bottom line is that in my example above, since T is fixed to int, your method becomes:
void insert(int&& obj) { storage[size++] = std::forward<int>(obj); }
This is not a forwaring reference at all, but simply takes by rvalue reference, i.e. it only binds to rvalues. That is why you typically see two overloads for things like push_back, one for lvalues and one for rvalues.
#Nir Friedman already answered the question, so I'm going to offer some additional advice.
If your Container class is not meant to store polymorphic types (which is common of containers, including std::vector and other similar STL containers), you can get away with simplifying your code, in the way you're trying to do in your original example.
Instead of:
void insert(T const& t) {
storage[size++] = t;
}
void insert(T && t) {
storage[size++] = std::move(t);
}
You could get perfectly correct code by writing the following instead:
void insert(T t) {
storage[size++] = std::move(t);
}
The reason for this is that if the object is being copied in, t will be copy-constructed with the object provided, and then move-assigned into storage[size++], whereas if the object is being moved in, t will be move-constructed with the object provided, and then move-assigned into storage[size++]. So you've simplified your code at the cost of a single extra move-assignment, which many compilers will happily optimize out.
There is a major downside to this approach, though: If the object defines a copy-constructor and doesn't define a move-constructor (common for older types in legacy code), this results in double-copies in all cases. Your compiler might be able to optimize it away (because compilers can optimize to completely different code so long as the user-visible effects are unchanged), but maybe not. That could be a significant performance hit if you have to work with heavy objects that don't implement move-semantics. This is probably the reason STL containers don't use this technique (they value performance over brevity). But if you're looking for a way to reduce the amount of boilerplate code you write, and aren't worried about having to use "copy-only" objects, then this will probably work fine for you.

Setters in template classes

I'm trying to find a good way to write a setter function for a template class. For non-template classes if somehow trivial because the function signature/implementation depends on parameter type. For example if the parameter type is int the next function should be optimal:
void MyClass::Set(int value)
{
myValue = value;
}
If the parameter type is std::vector next implementation should be close to optimal:
void MyClass::Set(std::vector<SomeType> value)
{
std::swap(myValue, value);
}
as the right constructor (move or copy) will be used to construct the function parameter and no unnecessary copying occurs, assuming move constructor cost is negligible.
As you can see both implementations have drawbacks when the type is changed: If the type is changed to std::vector for the first version, at least one unnecessary copy is made increasing the actual cost by a factor of 2 or 3.
If the type changed to int in the second version 2 unnecessary copies are made, increasing the actual cost by a factor of 3.
Can you please give me a good generic implementation for a setter function (maybe with overloads)? It should be optimal/close to optimal for any type used as parameter.
PS: I would prefer to not use std::enable_if to make several type dependent setters as the class will increase dramatically.
You can use a forwarding reference to accept either an rvalue or an lvalue, then forward that to the appropriate move or copy assignment operator:
template <typename T>
void Set(T && value) {
myValue = std::forward<T>(value);
}
You should have 3 overloads.
void Set( T&& t ) {
v = std::move(t);
}
void Set( T const& t ) {
v = t;
}
template<class U>
void Set( U&& u ) {
v = std::forward<U>(u);
}
the first two allow implicit conversion to work well, if the caller uses {} initialization on the argument, or if the caller is the name of a function and it needs a context, or a few other cases: it will work. This fixes some of the bigger annoyances with "perfect forwarding".
The third gives you anything for which your operator= is overloaded to handle that isn't covered by the first two. It is an example of "perfect forwarding", which as the name implies is imperfect.
The most common problem if you only use the 3rd overload, .Set( {construction args} ) doesn't work.
Possibly the const& overload is redundant, but I am unsure.
live example
// and sometimes:
template<class...Us>
void Set( Us&&...us ) {
v = {std::forward<Us>(us)...};
}
The forth is an emplace-set. This is not actually useful in this case (where we are using operator=), but in some cases it can be. For an example of where such an emplace-accessor is useful, optional::value_or should have that overload. Basically, cases where you could directly construct the target value, or where other parameters might lead to you not constructing the value, this is useful: if you do a {std::forward<Us>(us)...} always you might as well use .Set( {args...} ) externally, which calls the first overload above.

Sharing implementations for const lvalue (const T&) and rvalue (T&&) overloads: just like what is done for const and non-const overloads

Background
The following code block appears in Scott Meyers' famous book "Effective C++" Item 3:
class TextBlock {
public:
...
const char& operator[](std::size_t position) const
{
... // do bounds checking
... // log access data
... // verify data integrity
return text[position];
}
char& operator[](std::size_t position)
{
... // do bounds checking
... // log access data
... // verify data integrity
return text[position];
}
...
private:
std::string text;
};
The author states that in the above implementation, the contents of the const and the non-const overloads are essentially the same. In order to avoid code duplication, it could be simplified like this:
class TextBlock {
public:
...
const char& operator[](std::size_t position) const // the same as before
{
...
...
...
return text[position];
}
char& operator[](std::size_t position) // now just calls const op[]
{
return // cast away const on
const_cast<char&>( // op[]'s return type;
static_cast<const TextBlock&>(*this) // add const to *this's type;
[position] // call const version of op[]
);
}
...
private:
std::string text;
};
Questions
My questions are:
When shall we need an overload for const T& and another for T&&? (Here, T may be a template parameter or a class type, so T&& may or may not mean a universal reference) I can see that in the standard library, many classes provide both overloads. Examples are constructors of std::pair and std::tuple, there are tons of overloads. (Okay, I know that among the functions, one of them is the copy constructor and one of them is the move constructor.)
Is there a similar trick to share the implementations for the const T& and T&& overloads? I mean, if the const T&& overload returns an object that is copy-constructed, and the T&& overload returns something that is move-constructed, after sharing the implementation, this property must still hold. (Just like the above trick: const returns const and non-const returns non-const, both before and after implementation sharing)
Thanks!
Clarifications
The two overloads I'm referring to should look like:
Gadget f(Widget const& w);
Gadget f(Widget&& w);
It is nothing related to returning by rvalue references, that is:
Widget&& g(/* ... */);
(By the way, that question was addressed in my previous post)
In f() above, if Gadget is both copy-constructible and move-constructible, there's no way (except from reading the implementation) to tell whether the return value is copy-constructed or move-constructed. It is nothing to deal with Return Value Optimization (RVO) / Named Return Value Optimization (NRVO). (See my previous post)
References
Effective C++
std::pair::pair
std::tuple::tuple
When is it a good time to return by rvalue references?
• When shall we need an overload for const T& and another for T&&?
Basically, when moving gives you a performance gain, there should be also a move constructor. The same holds for functions in which you'd otherwise needed an expensive copy.
In your example, where you return a reference to a char, it is however not advisable to also set up a function which returns an rvalue reference. Rather, return by value and rely on the compiler's ability to apply RVO (see e.g. here)
•Is there a similar trick to share the implementations for the const T& and T&& overloads?
I often found it useful to set up a constructor or function using a universal reference (I'm lazy), i.e. something like
struct MyClass
{
template<typename T /*, here possibly use SFINAE to allow only for certain types */>
MyClass(T&& _t) : t(std::forward<T>(_t)) {}
private:
SomeType t;
};
EDIT: Regarding your update: if you have an expensive copy of Widget in your function f, it is adviceable also to provide an overload taking a Widget&&.
Gadget f(Widget const& w)
{
Widget temp = w; //expensive copy
}
Gadget f(Widget&& w)
{
Widget temp = std::move(w); //move
}
You could combine both function using a function template like this
template<typename WidgetType
// possibly drop that SFINAE stuff
// (as it is already checked in the first assignment)
, typename std::enable_if<std::is_convertible<std::remove_reference_t<WidgetType>, Widget>::value> >
Gadget(WidgetType&& w)
{
Widget temp = std::forward<WidgetType>(w);
//or
std::remove_reference_t<WidgetType> temp2 = std::forward<WidgetType>(w);
}
... I didn't say it's nicer ;-).
EDIT 2: See also this thread, which addresses your question much more thouroughly.