I am trying to understand how std::declval<T>() works. I know how to use it, and know what it does, mainly allows you to use decltype without constructing the object, like
decltype(std::declval<Foo>().some_func()) my_type; // no construction of Foo
I know from cppreference.com that std::declval<Foo> "adds" a rvalue reference to Foo, which due to reference collapsing rules ends up being either a rvalue reference or a lvalue reference. My question is why the constructor of Foo is not called? How can one implement a "toy" version of std::declval<T> without constructing the template parameter?
PS: I know it is not the same as the old trick
(*(T*)(nullptr))
Basically, in a sizeof or decltype expression you can call functions that aren't implemented anywhere (they need to be declared, not implemented).
E.g.
class Silly { private: Silly( Silly const& ) = delete; };
auto foo() -> Silly&&;
auto main() -> int
{
sizeof( foo() );
}
The linker should not complain about that.
Related
I defined a class that receives an lambda function through constructor. The code is as follows. Why did the definition of t0 pass compilation after using the std::forward, and t1 incur an error?
#include <iostream>
template <typename Func>
class Test {
public:
Test(Func &&func) : m_func(std::forward<Func &&>(func)) {}
void Run() { m_func(); }
private:
Func &&m_func;
};
template <typename Func>
class Foo {
public:
Foo(Func &func) : m_func(func) {}
void Run() { m_func(); }
private:
Func &m_func;
};
int main() {
const auto print = []() { std::cout << "Hello" << std::endl; };
using Print = decltype(print);
Test<decltype(print)> t0(std::forward<Print&&>(print));
t0.Run();
Test<void()> t1(Print{});
t1.Run();
Foo<decltype(print)> t3(std::forward<Print&&>(print));
t3.Run();
Foo<void()> t4(Print{});
t4.Run();
}
[Update]
The definition of t1 should be as following. thx for #JaMiT.
Test<void(*)()> t1([]() { std::cout << "Hello" << std::endl; });
But I'm still confused about the definition of t0. If I deletes the std::forward, it incurs a compilation error.
[Update]
It works if I change the definition of t0 to Test<void (*)()> t0(print);. What's the difference between Test<decltype(print)> t0(print); that causes a compilation error?
Why did the definition of t0 pass compilation after using the std::forward,
Because that is how you declared the constructor of Test. The constructor takes as its parameter an rvalue reference to the template parameter. You explicitly provided decltype(print) as the template argument, so the constructor takes an rvalue of that type. There will be no copying (no pass by value), and an lvalue reference will not cut it. You must provide an rvalue.
By adding std::forward<Print&&>, you converted print to an rvalue. (It would have been simpler to add std::move instead. Rule of thumb: use std::forward when dealing with a "placeholder" type, such as a template parameter, and use std::move when dealing with a fixed type.)
Caution: After using std::forward<Print&&>(print) or std::move(print), you should treat print as uninitialized. That is, your initialization of t3 is a potential bug.
Another tweak that would make this compile is to specify decltype(print)& (with the ampersand at the end) as the template argument. When Func is an lvalue reference, Func&& collapses to Func, which means the constructor would take an lvalue reference instead of an rvalue reference. (Reference collapsing is a key component of forwarding references, on which perhaps you based your code? However, forwarding references would require the constructor to itself be a template.)
and t1 incur an error?
For t1, you specified the template argument as void(), which is the type of a function. Lambdas are objects, not functions, so there is a type mismatch.
On the other hand, a lambda with no captures (nothing inside the []) can implicitly convert to a pointer to a function. This is a place where confusion lurks, because functions also decay to pointers so people can get used to interchanging function types and pointer to function types. To specify a pointer to a function, use void(*)() instead of void().
Caution: Implicit conversions can wreak havoc when combined with references. Then again, you were already in the danger zone when you combined temporary objects (Print{}) with references. Your code would be safer if you changed the data member to Func m_func;. In addition to avoiding dangling references, this would be more efficient (less indirection) when storing a pointer-to-function.
template <typename Func>
class Test {
public:
// Constructor can still take a reference and forward it to the member.
Test(Func &&func) : m_func(std::forward<Func &&>(func)) {}
void Run() { m_func(); }
private:
Func m_func; // Not necessarily a reference
};
There are still potential issues (e.g. Func could be specified as a reference type), but at least this is safer. I choose to treat the remaining issues as out-of-scope for this question about syntax.
It works if I change the definition of t0 to Test<void (*)()> t0(print);.
This combines some concepts I presented earlier. The template argument is now a pointer to a function, so your lambda (print) will undergo an implicit conversion, similar to the t1 case. The result of an implicit conversion is an rvalue, which is what your constructor expects (no need to forward or move).
Caution: By "works", you really mean "compiles". The fact that you asked this question suggests you already know the following, but for the benefit of others: getting code to compile is a necessary step, but that by itself does not mean the code is correct and works as intended. Don't be satisfied when a tweak you do not understand makes your code compile – ask questions!
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…
Given the following function template from "The C++ Programming language 4th edition":
template <typename TT, typename A>
unique_ptr<TT> make_unique(int i, A && a)
{
return unique_ptr<TT>{new TT{ i, std::forward<A>(a) }};
}
I find it difficult to understand what that actually does,
a is definitely an rvalue and therefore the make_unique function
seem to allocate its content on the heap and holding that address in a unique_ptr so we won't have to worry about deleting it. but, what does the standard library forward function does? (I guess it has something to do with a being rvalue) I tried reading at C++ documentation but I don't seem to understand that properly.
would love to get a good explanation from a more experienced C++ programmer.
thanks!
Hmmm... I'm pretty sure this isn't given as a work-around implementation of the future std::make_unique, but anyway, what the function does is pretty easy to understand, though it requires you to have prior knowledge of new C++11 features.
template <typename TT, typename A>
unique_ptr<TT> make_unique(int i, A && a)
{
return unique_ptr<TT>{new TT{ i, std::forward<A>(a) }};
}
First of all make_unique is a function template, I really hope you already know that, as the following would require that you have at least the most basic knowledge on what templates does and how templates work.
Now to the non-trivial parts. A && a there is a function parameter. Specifically, a is the function parameter whose type is A&& which is an r-value reference. With its type being a template type parameter, we can deduce its type from whatever the caller passes as an argument to a. Whenever we have r-value reference and argument type deduction, special deduction rules and reference collapsing kicks-in and we have a so-called "universal reference" which is particularly useful for perfect forwarding functions.
Whenever we have a universal reference (a in our case), we will almost always want to preserve its original "l-valueness" or "r-valueness" whenever we want to use them. To have this kind of behavior, we should almost always use std::forward (std::forward<A>(a)). By using std::forward, a variable originally passed as an l-value remains an l-value and a variable originally passed as an r-value remains an r-value.
After that, things are just simple
return unique_ptr<TT>{new TT{ i, std::forward<A>(a) }};
Notice the use of the braces. Instead of using parentheses, it is using C++11's uniform initialization syntax of calling constructors. With new TT{ i, std::forward<A>(a) }, you are dynamically allocating an object of type TT with the given parameters inside the braces. With unique_ptr<TT>{new TT{ i, std::forward<A>(a) }};, you are creating a unique_ptr<TT> whose parameter is the one returned by the dynamic allocation. The unique_ptr<TT> object now then returned from the function.
Due to template argument deduction and reference collapsing rules you cannot know if a is a rvalue reference or a lvalue reference. std::forward passes the argument to the TT contrustor exactly as it was passed to make_unique. Scott Meyers calls A&& a universal reference, because it can be a lvalue ref or an rvalue ref, depended on what is passed to make_unique.
If you pass an rvalue Foo to make_unique, std::forward passes an rvalue reference.
If you pass an lvalue Foo to make_unique, std::forward passes an lvalue reference.
make_unique(1, Foo()); // make_unique(int, A&&) -> rvalue ref
Foo f;
make_unique(1, f); // make_unique(int, A&&&) -> make_unique(int, A&) -> lvalue ref
make_unique(1, std::move(f)); // make_unique(int, A&&&&) -> make_unique(int, A&&) -> rvalue ref
I've come across this syntactic construct a few times, and I'm wondering:
What does this do?
What might the design reasoning be?
It tends to look something like this:
struct SubType : public SomeSuperType {
SubType(int somthing) : SuperType(something), m_foo(*((FooType *)0))
{}
private:
FooType m_foo;
}
To be clear, the code works. But what's the purpose? What would be the status of m_foo without that line?
The purpose of this construct is to emulate a fake unnamed object of type SomeType in situations when you formally need an object, but don't want or can't declare a real one. It has its valid uses and does not necessarily cause undefined behavior.
A classic example would be determining the size of some class member
sizeof (*(SomeClass *) 0).some_member
or a similar application of decltype
decltype((*(SomeClass *) 0).some_member)
Neither of the above examples causes any undefined behavior. In non-evaluated context expressions like *(SomeClass *) 0 are perfectly legal and valid.
You can also see this technique used for illustrative purposes in the language standard itself, as in 8.3.5/12
A trailing-return-type is most useful for a type that would be more
complicated to specify before the declarator-id:
template <class T, class U> auto add(T t, U u) -> decltype(t + u);
rather than
template <class T, class U> decltype((*(T*)0) + (*(U*)0)) add(T t, U u);
Observe how the (*(T*)0) + (*(U*)0) expression is used under decltype to perform compile-time prediction of the result type of binary + operator between types T and U.
Of course, again, such tricks are only valid when used in non-evaluated contexts, as shown above.
Sometimes it is used as an initializer for "null references" as in
SomeType &r = *(SomeType *) 0;
but this actually crosses the boundary of what's legal and produces undefined behavior.
What you have in your specific example is invalid, since it attempts to access an invalid "null lvalue" in evaluated context.
P.S. In C language there's also that peculiar part of specification that says that operators & and * cancel each other, meaning that &*(SomeType *) 0 is valid and guaranteed to evaluate to null pointer. But it does not extend to C++.
What does this do? Undefined behaviour.
What might the design reasoning be? A desire to cause undefined behaviour. There's no other rationale.
I don't think the example is necessarily UB. It depends on the definition of FooType. Suppose Foo is an empty class with a constructor that does something:
class Foo {
public:
Foo() { std::cout << "Hey, world! A new Foo just arrived.\n"; }
// I think the default copy and assign constructors do nothing
// with an empty type, but just in case:
Foo(const Foo&) {}
Foo& operator=(const Foo&) { return *this; }
};
Now, suppose I need a Foo, for whatever reason, and I don't want to trigger the constructor. Doing this will not cause any actual dereferencing because operator* does not dereference and the copy constructor doesn't use its reference argument:
Foo(*static_cast<Foo*>(0));
If you read code like
auto&& var = foo();
where foo is any function returning by value of type T. Then var is an lvalue of type rvalue reference to T. But what does this imply for var? Does it mean, we are allowed to steal the resources of var? Are there any reasonable situations when you should use auto&& to tell the reader of your code something like you do when you return a unique_ptr<> to tell that you have exclusive ownership? And what about for example T&& when T is of class type?
I just want to understand, if there are any other use cases of auto&& than those in template programming; like the ones discussed in the examples in this article Universal References by Scott Meyers.
By using auto&& var = <initializer> you are saying: I will accept any initializer regardless of whether it is an lvalue or rvalue expression and I will preserve its constness. This is typically used for forwarding (usually with T&&). The reason this works is because a "universal reference", auto&& or T&&, will bind to anything.
You might say, well why not just use a const auto& because that will also bind to anything? The problem with using a const reference is that it's const! You won't be able to later bind it to any non-const references or invoke any member functions that are not marked const.
As an example, imagine that you want to get a std::vector, take an iterator to its first element and modify the value pointed to by that iterator in some way:
auto&& vec = some_expression_that_may_be_rvalue_or_lvalue;
auto i = std::begin(vec);
(*i)++;
This code will compile just fine regardless of the initializer expression. The alternatives to auto&& fail in the following ways:
auto => will copy the vector, but we wanted a reference
auto& => will only bind to modifiable lvalues
const auto& => will bind to anything but make it const, giving us const_iterator
const auto&& => will bind only to rvalues
So for this, auto&& works perfectly! An example of using auto&& like this is in a range-based for loop. See my other question for more details.
If you then use std::forward on your auto&& reference to preserve the fact that it was originally either an lvalue or an rvalue, your code says: Now that I've got your object from either an lvalue or rvalue expression, I want to preserve whichever valueness it originally had so I can use it most efficiently - this might invalidate it. As in:
auto&& var = some_expression_that_may_be_rvalue_or_lvalue;
// var was initialized with either an lvalue or rvalue, but var itself
// is an lvalue because named rvalues are lvalues
use_it_elsewhere(std::forward<decltype(var)>(var));
This allows use_it_elsewhere to rip its guts out for the sake of performance (avoiding copies) when the original initializer was a modifiable rvalue.
What does this mean as to whether we can or when we can steal resources from var? Well since the auto&& will bind to anything, we cannot possibly try to rip out vars guts ourselves - it may very well be an lvalue or even const. We can however std::forward it to other functions that may totally ravage its insides. As soon as we do this, we should consider var to be in an invalid state.
Now let's apply this to the case of auto&& var = foo();, as given in your question, where foo returns a T by value. In this case we know for sure that the type of var will be deduced as T&&. Since we know for certain that it's an rvalue, we don't need std::forward's permission to steal its resources. In this specific case, knowing that foo returns by value, the reader should just read it as: I'm taking an rvalue reference to the temporary returned from foo, so I can happily move from it.
As an addendum, I think it's worth mentioning when an expression like some_expression_that_may_be_rvalue_or_lvalue might turn up, other than a "well your code might change" situation. So here's a contrived example:
std::vector<int> global_vec{1, 2, 3, 4};
template <typename T>
T get_vector()
{
return global_vec;
}
template <typename T>
void foo()
{
auto&& vec = get_vector<T>();
auto i = std::begin(vec);
(*i)++;
std::cout << vec[0] << std::endl;
}
Here, get_vector<T>() is that lovely expression that could be either an lvalue or rvalue depending on the generic type T. We essentially change the return type of get_vector through the template parameter of foo.
When we call foo<std::vector<int>>, get_vector will return global_vec by value, which gives an rvalue expression. Alternatively, when we call foo<std::vector<int>&>, get_vector will return global_vec by reference, resulting in an lvalue expression.
If we do:
foo<std::vector<int>>();
std::cout << global_vec[0] << std::endl;
foo<std::vector<int>&>();
std::cout << global_vec[0] << std::endl;
We get the following output, as expected:
2
1
2
2
If you were to change the auto&& in the code to any of auto, auto&, const auto&, or const auto&& then we won't get the result we want.
An alternative way to change program logic based on whether your auto&& reference is initialised with an lvalue or rvalue expression is to use type traits:
if (std::is_lvalue_reference<decltype(var)>::value) {
// var was initialised with an lvalue expression
} else if (std::is_rvalue_reference<decltype(var)>::value) {
// var was initialised with an rvalue expression
}
First, I recommend reading this answer of mine as a side-read for a step-by-step explanation on how template argument deduction for universal references works.
Does it mean, we are allowed to steal the resources of var?
Not necessarily. What if foo() all of a sudden returned a reference, or you changed the call but forgot to update the use of var? Or if you're in generic code and the return type of foo() might change depending on your parameters?
Think of auto&& to be exactly the same as the T&& in template<class T> void f(T&& v);, because it's (nearly†) exactly that. What do you do with universal references in functions, when you need to pass them along or use them in any way? You use std::forward<T>(v) to get the original value category back. If it was an lvalue before being passed to your function, it stays an lvalue after being passed through std::forward. If it was an rvalue, it will become an rvalue again (remember, a named rvalue reference is an lvalue).
So, how do you use var correctly in a generic fashion? Use std::forward<decltype(var)>(var). This will work exactly the same as the std::forward<T>(v) in the function template above. If var is a T&&, you'll get an rvalue back, and if it is T&, you'll get an lvalue back.
So, back on topic: What do auto&& v = f(); and std::forward<decltype(v)>(v) in a codebase tell us? They tell us that v will be acquired and passed on in the most efficient way. Remember, though, that after having forwarded such a variable, it's possible that it's moved-from, so it'd be incorrect use it further without resetting it.
Personally, I use auto&& in generic code when I need a modifyable variable. Perfect-forwarding an rvalue is modifying, since the move operation potentially steals its guts. If I just want to be lazy (i.e., not spell the type name even if I know it) and don't need to modify (e.g., when just printing elements of a range), I'll stick to auto const&.
† auto is in so far different that auto v = {1,2,3}; will make v an std::initializer_list, whilst f({1,2,3}) will be a deduction failure.
Consider some type T which has a move constructor, and assume
T t( foo() );
uses that move constructor.
Now, let's use an intermediate reference to capture the return from foo:
auto const &ref = foo();
this rules out use of the move constructor, so the return value will have to be copied instead of moved (even if we use std::move here, we can't actually move through a const ref)
T t(std::move(ref)); // invokes T::T(T const&)
However, if we use
auto &&rvref = foo();
// ...
T t(std::move(rvref)); // invokes T::T(T &&)
the move constructor is still available.
And to address your other questions:
... Are there any reasonable situations when you should use auto&& to tell the reader of your code something ...
The first thing, as Xeo says, is essentially I'm passing X as efficiently as possible, whatever type X is. So, seeing code which uses auto&& internally should communicate that it will use move semantics internally where appropriate.
... like you do when you return a unique_ptr<> to tell that you have exclusive ownership ...
When a function template takes an argument of type T&&, it's saying it may move the object you pass in. Returning unique_ptr explicitly gives ownership to the caller; accepting T&& may remove ownership from the caller (if a move ctor exists, etc.).
The auto && syntax uses two new features of C++11:
The auto part lets the compiler deduce the type based on the context (the return value in this case). This is without any reference qualifications (allowing you to specify whether you want T, T & or T && for a deduced type T).
The && is the new move semantics. A type supporting move semantics implements a constructor T(T && other) that optimally moves the content in the new type. This allows an object to swap the internal representation instead of performing a deep copy.
This allows you to have something like:
std::vector<std::string> foo();
So:
auto var = foo();
will perform a copy of the returned vector (expensive), but:
auto &&var = foo();
will swap the internal representation of the vector (the vector from foo and the empty vector from var), so will be faster.
This is used in the new for-loop syntax:
for (auto &item : foo())
std::cout << item << std::endl;
Where the for-loop is holding an auto && to the return value from foo and item is a reference to each value in foo.