Why is std::forward turning my lvalue into an rvalue? - c++

template<class T>
struct IntHolder {
T i;
};
template<class T>
void addOne(T& t) {
t.i += 1;
}
template<class... Args>
void addAll(Args... args) {
// Magic to run addOne on each arg
int dummy[] = { 0, ((void)addOne(std::forward<Args>(args)), 0)... };
}
int main() {
IntHolder<int> x{2};
IntHolder<double> t{3};
addAll(x, t);
return 0;
}
This toy example won't compile because
prog.cc: In instantiation of 'void addAll(Args ...) [with Args = {IntHolder<int>, IntHolder<double>}]':
prog.cc:60:16: required from here
prog.cc:54:39: error: invalid initialization of non-const reference of type 'IntHolder<int>&' from an rvalue of type 'IntHolder<int>'
int dummy[] = { 0, ((void)addOne(std::forward<Args>(args)), 0)... };
^
prog.cc:48:6: note: initializing argument 1 of 'void addOne(T&) [with T = IntHolder<int>]'
void addOne(T& t) {
What I thought would happen here is that addAll gets two lvalues passed in, and then addOne would be called on each of those as an lvalue. However, it seems somewhere along the way, the compiler thinks that the argument is getting converted to an rvalue. Why might this be?

You're declaring parameter as non-reference, i.e. pass-by-value, then when being passed x and t, the type would be deduced as IntHolder<int> and IntHolder<double>. Then std::forward<Args> would convert them into rvalues.
For forwarding reference it should be
template<class... Args>
void addAll(Args&&... args) {
// ^^
// Args would be deduced as IntHolder<int>&, IntHolder<double>&
// then std::forward<Args>(args) yields lvalues
int dummy[] = { 0, ((void)addOne(std::forward<Args>(args)), 0)... };
}

Related

decltype(auto) in function return type does not deduces const types

In following code:
template<typename T>
struct TD;
decltype(auto) getMyConst() {
const int val = 10;
return (const int)val; // const int cast is not needed here actually
}
int main()
{
TD<decltype(getMyConst())> type_test;
}
produces error:
main.cpp:41:32: error: implicit instantiation of undefined template 'TD<int>'
TD<decltype(getMyConst())> type_test;
which indicates that deduced type is int, I would expect decltype rules to deduce const int. Why does clang compiler deduce 'int'?

Recursive templated function cannot assign to variable 'state' with const-qualified type 'const tt &'

I've reduced my code down to the following minimum code:
#include<iostream>
#include<vector>
class tt
{
public:
bool player;
std::vector<tt> actions;
};
template<typename state_t>
int func(state_t &state, const bool is_max)
{
state.player = true;
const auto &actions = state.actions;
if(state.actions.size())
{
auto soln = func(actions[0], false);
}
return 0;
}
int main(int argc, char const *argv[])
{
tt root;
func(root, true);
return 0;
}
When I try to compile this code, I get
test.cpp:14:17: error: cannot assign to variable 'state' with const-qualified type 'const tt &'
state.player = true;
~~~~~~~~~~~~ ^
test.cpp:19:19: note: in instantiation of function template specialization 'func<const tt>' requested here
auto soln = func(actions[0], false);
^
test.cpp:28:4: note: in instantiation of function template specialization 'func<tt>' requested here
func(root, true);
^
test.cpp:12:19: note: variable 'state' declared const here
int func(state_t &state, const bool is_max)
~~~~~~~~~^~~~~
1 error generated.
It is claiming that state is a const tt & type. The signature of the templated function is int func(state_t &state, const bool is_max), and there is no const in front of the state_t. It appears the const is somehow being deduced from the recursive call because actions is a const-ref vector of tt objects. I thought argument deduction ignores const? How can this occur?
Answer is mainly extracted from Scott Mayers Effective C++ book.
template<typename T>
void f(ParamType param);
f(expr); // deduce T and ParamType from expr
ParamType is a Reference or Pointer, but not a Universal Reference
The simplest situation is when ParamType is a reference type or a pointer type, but not a universal reference. In that case, type deduction works like this:
If expr’s type is a reference, ignore the reference part.
Then pattern-match expr’s type against ParamType to determine T.
In argument deduction process it ignores the reference part not the const part.
In your case it is const auto &actions = state.actions; which means, for the template argument deduction of auto soln = func(actions[0], false); only the reference part is dropped, not the cv qualifiers.
Further examples from the book.
template<typename T>
void f(T& param); // param is a reference
and we have these variable declarations,
int x = 27; // x is an int
const int cx = x; // cx is a const int
const int& rx = x; // rx is a reference to x as a const int
the deduced types for param and T in various calls are as follows:
f(x); // T is int, param's type is int&
f(cx); // T is const int,
// param's type is const int&
f(rx); // T is const int,
// param's type is const int&
In addition to #aep's answer, If it had been deduced as tt instead of const tt, the compiler would generated an error too, because it is not possible to bind const reference to non-const reference without const_cast.
#include<iostream>
#include<vector>
class tt
{
public:
bool player;
std::vector<tt> actions;
};
void try_to_bind_const_reference_to_non_const_reference( tt& t )
{
}
template<typename state_t>
int func(state_t &state, const bool is_max)
{
state.player = true;
const auto &actions = state.actions;
if(state.actions.size())
{
// auto soln = func(actions[0], false);
try_to_bind_const_reference_to_non_const_reference( actions[0] );
}
return 0;
}
int main(int argc, char const *argv[])
{
tt root;
func(root, true);
return 0;
}
run online

C++ template parameter pack automatically adds & to its parameters

Check the below code.
#include <future>
template <class F, class... Args>
void do_something(F f, Args... args) {
using return_type = typename std::result_of<F(Args...)>::type;
// Why below gives an error?
std::packaged_task<return_type(Args...)> task(f, args...);
}
int func(int a, int b) {
}
int main() {
do_something(func, 1, 2);
}
The packaged_task constructor gives a following error.
error: no matching function for call to 'std::packaged_task<int(int, int)>::packaged_task(int (*&)(int, int), int&, int&)'
8 | std::packaged_task<return_type(Args...)> task(f, args...);
The thing I don't understand is that why f and args became a reference type in the constructor? The Args... were int, int types whereas args... just became int&, int&. Where is this coming from?
packaged_task do not have the signature you want.
compiler is saying there is no such function. (probably with candidates below it)

Generic Exponential Backoff Retry Mechanism C++11

I have written a generic exponential backoff retry loop in C++11. I'm using std::function to pass the callable to retry loop. callable will be retried if isRetriable function returns true.
#include <algorithm>
#include <cassert>
#include <chrono>
#include <functional>
#include <iostream>
#include <thread>
constexpr int64_t max_backoff_milliseconds = 30000; // 30 seconds
template <class R, class... Args>
R Retry(int max_retry_count, int64_t initial_dealy_milliseconds,
const std::function<bool(R)> &isRetriable,
const std::function<R(Args...)> &callable, Args &&... args)
{
int retry_count = 0;
while (true) {
auto status = callable(std::forward<Args>(args)...);
if (!IsRetriable(status)) {
return status;
}
if (retry_count >= max_retry_count) {
// Return status and abort retry
return status;
}
int64_t delay_milliseconds = 0;
if (initial_dealy_milliseconds > 0) {
delay_milliseconds =
std::min(initial_dealy_milliseconds << retry_count,
max_backoff_milliseconds);
}
std::cout << "Callable execution failed. Retry Count:"
<< retry_count + 1 << std::endl;
std::this_thread::sleep_for(
std::chrono::milliseconds(delay_milliseconds));
retry_count++;
}
}
bool isRetriable(int status) {
if (status == 5)
return true;
return false;
}
int foo(int x, int y) {
static int a = 1;
a += (x + y);
return a / 6;
}
int main() {
auto result = Retry(1000, 100, isRetriable, foo, 1, 3);
std::cout << result << std::endl;
return 0;
}
When I compile it, I'm getting below error:
prog.cpp: In function ‘int main()’:
prog.cpp:50:71: error: no matching function for call to ‘Retry(int,
int, bool (&)(int), int (&)(int, int), int, int)’
auto result = Retry<int, int, int>(1000, 100, isRetriable, foo, 1, 3);
^
prog.cpp:11:3: note: candidate: template<class R, class ... Args> R
Retry(int, int64_t, const std::function<bool(R)>&, const
std::function<_Res(_ArgTypes ...)>&, Args&& ...)
R Retry(int max_retry_count,
^~~~~
prog.cpp:11:3: note: template argument deduction/substitution failed:
prog.cpp:50:71: note: mismatched types ‘const
std::function<int(_ArgTypes ...)>’ and ‘int(int, int)’
auto result = Retry<int, int, int>(1000, 100, isRetriable, foo, 1, 3);
^
Could someone explain to me why I have this error?
I'm sure there's a good duplicate for this but...
Here's a shorter reproduction:
template <typename T> void foo(std::function<bool(T)> ) { }
bool maybe(int ) { return false; }
foo(maybe); // error: no matching function call to 'foo(bool (&)(int))'
You may ask - what?! maybe is something that's callable with some T that returns bool. But that's not how template deduction works. In order to deduce std::function<bool(T)> against an argument, that argument needs to be a std::function. maybe isn't a std::function, it's just a function, so that deduction fails. Any kind of deduction with a different kind of expression will also fail:
foo([](int ) { return true; }); // also error
Basically, trying to deduce a std::function is almost always the wrong thing to do. First, it's wrong because it won't work. Second, it's wrong because even if it did work, you're incurring type erasure in a context in which you probably don't need it.
What you want to do instead is deduce arbitrary callables, and then determine what these other arguments are based on those callables. The return type of callable is just what you get when you call callable with Args..., and you want to ensure that isRetriable is a predicate on that type.
One approach to that is:
template <typename Predicate, typename Callable, typename... Args,
// figure out what the callable returns
typename R = std::decay_t<std::invoke_result_t<Callable&, Args...>>,
// require that Predicate is actually a Predicate
std::enable_if_t<
std::is_convertible_v<std::invoke_result_t<Predicate&, R>, bool>,
int> = 0>
R Retry(int max_retry_count, int64_t initial_dealy_milliseconds,
Predicate&& isRetriable,
Callable&& callable,
Args&&... args)
{
// ....
}

Pass lambda as template function parameter

Why doesn't the following code compile (in C++11 mode)?
#include <vector>
template<typename From, typename To>
void qux(const std::vector<From>&, To (&)(const From&)) { }
struct T { };
void foo(const std::vector<T>& ts) {
qux(ts, [](const T&) { return 42; });
}
The error message is:
prog.cc:9:5: error: no matching function for call to 'qux'
qux(ts, [](const T&) { return 42; });
^~~
prog.cc:4:6: note: candidate template ignored: could not match 'To (const From &)' against '(lambda at prog.cc:9:13)'
void qux(const std::vector<From>&, To (&)(const From&)) { }
^
But it doesn't explain why it couldn't match the parameter.
If I make qux a non-template function, replacing From with T and To with int, it compiles.
A lambda function isn't a normal function. Each lambda has its own type that is not To (&)(const From&) in any case.
A non capturing lambda can decay to To (*)(const From&) in your case using:
qux(ts, +[](const T&) { return 42; });
As noted in the comments, the best you can do to get it out from a lambda is this:
#include <vector>
template<typename From, typename To>
void qux(const std::vector<From>&, To (&)(const From&)) { }
struct T { };
void foo(const std::vector<T>& ts) {
qux(ts, *+[](const T&) { return 42; });
}
int main() {}
Note: I assumed that deducing return type and types of the arguments is mandatory for the real problem. Otherwise you can easily deduce the whole lambda as a generic callable object and use it directly, no need to decay anything.
If you don't need to use the deduced To type, you can just deduce the type of the whole parameter:
template<typename From, typename F>
void qux(const std::vector<From>&, const F&) { }
Correct me if I am wrong, but template parameters deduction deduces only exact types without considering possible conversions.
As a result the compiler cannot deduce To and From for To (&)(const From&) because qux expects a reference to function, but you provide a lambda which has its own type.
You have left absolutely no chance to compiler to guess what is To. Thus, you need to specify it explicitly.
Also, lambda here needs to be passed by pointer.
Finally, this version compiles ok:
template<typename From, typename To>
void qux(const std::vector<From>&, To (*)(const From&)) { }
struct T { };
void foo(const std::vector<T>& ts) {
qux<T,int>(ts,[](const T&) { return 42; });
}
You're expecting both implicit type conversions (from unnamed function object type to function reference type) and template type deduction to happen. However, you can't have both, as you need to know the target type to find the suitable conversion sequence.
But it doesn't explain why it couldn't match the parameter.
Template deduction tries to match the types exactly. If the types cannot be deduced, deduction fails. Conversions are never considered.
In this expression:
qux(ts, [](const T&) { return 42; });
The type of the lambda expression is some unique, unnamed type. Whatever that type is, it is definitely not To(const From&) - so deduction fails.
If I make qux a non-template function, replacing From with T and To with int, it compiles.
That is not true. However, if the argument was a pointer to function rather than a reference to function, then it would be. This is because a lambda with no capture is implicitly convertible to the equivalent function pointer type. This conversion is allowed outside of the context of deduction.
template <class From, class To>
void func_tmpl(From(*)(To) ) { }
void func_normal(int(*)(int ) ) { }
func_tmpl([](int i){return i; }); // error
func_tmpl(+[](int i){return i; }); // ok, we force the conversion ourselves,
// the type of this expression can be deduced
func_normal([](int i){return i; }); // ok, implicit conversion
This is the same reason why this fails:
template <class T> void foo(std::function<T()> );
foo([]{ return 42; }); // error, this lambda is NOT a function<T()>
But this succeeds:
void bar(std::function<int()> );
bar([]{ return 42; }); // ok, this lambda is convertible to function<int()>
The preferred approach would be to deduce the type of the callable and pick out the result using std::result_of:
template <class From,
class F&&,
class To = std::result_of_t<F&&(From const&)>>
void qux(std::vector<From> const&, F&& );
Now you can pass your lambda, or function, or function object just fine.