Problem by a reference variable of a template parameter - c++

The following small example shows my problem:
template<class T> struct X
{
static void xxx(T& x) { }
static void xxx(T&& x) { }
};
int main(int argc, char** argv)
{
int x = 9;
X<int>::xxx(x); // OK.
X<int&>::xxx(x); // ERROR!
return 0;
}
Error message (GCC):
error: ‘static void X::xxx(T&&) [with T = int&]’ cannot be overloaded
error: with ‘static void X::xxx(T&) [with T = int&]’
Why? T = int& ---> Is T& replaced by int&& in static void xxx(T& x)?
If the answer to the question is yes, then:
T& is not a lvalue-reference and it becomes a rvalue-reference!
And the following code should work:
But it didn't:
template<class T> struct X
{
static void xxx(T& x) { }
};
int main(int argc, char** argv)
{
X<int&>::xxx(2); // ERROR!
return 0;
}
Error Message (GCC):
error: no matching function for call to ‘X::xxx(int)’
note: candidates are: static void X::xxx(T&) [with T = int&]
Then T& with T = int& is not equal to T&& and is not a rvalue-reference. but if it is not, why the first example not working? (it's a recursive problem!)
But the similar problem didn't occur for pointer types:
#include <iostream>
template<class T> struct X
{
static void xxx(T* x) { std::cout << **x << std::endl; }
};
int main(int argc, char** argv)
{
int x = 10;
int* xx = &x;
X<int*>::xxx(&xx); // OK. call X<int*>::xxx(int**)
return 0;
}
Why references are different in this behavior?

The C++11 language standard has an explanation of how this works at §8.3.2[dcl.ref]/6 (reformatted for readability):
If a typedef, a type template-parameter, or a decltype-specifier denotes a type TR that is a reference to a type T,
an attempt to create the type "lvalue reference to cv TR" creates the type "lvalue reference to T"
an attempt to create the type "rvalue reference to cv TR" creates the type TR.
Let's consider your example (I've renamed your T to be TR so it matches the language above):
template<class TR> struct X
{
static void xxx(TR& x) { }
static void xxx(TR&& x) { }
};
If we try instantiating X with TR = int& (so, T = int), the instantiations of xxx are as follows:
static void xxx(TR& x) { } --> static void xxx(int& x) { }
static void xxx(TR&& x) { } --> static void xxx(int& x) { }
In the first case, we attempt to create an "lvalue reference to TR," which becomes an "lvalue reference to T." T is int, so the parameter type becomes int&.
In the second case, we attempt to create an "rvalue reference to TR," which becomes TR, which is int&.
The parameter type is the same for both overloads, hence the error.

Related

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

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

Why this code snippet works with C++17 whereas the compiler complains when using C++11?

Why this code snippet works with C++17 whereas the compiler complains when using C++11(i.e https://godbolt.org/z/71G91P)?
Are there any potential problems with this code snippet?
#include<iostream>
class ctx
{
public:
int map_create(void*){std::cout << "haha" << std::endl; return 0;};
};
ctx obj;
typedef int (ctx::*ctx_mem_func)(void*);
template <ctx_mem_func func>
int regHelper(void*)
{
((&obj)->*func)(nullptr);
return 0;
}
constexpr ctx_mem_func testFunc = &ctx::map_create;
typedef int(*callBackFunc)(void*);
int reg(callBackFunc)
{
return 0;
}
int main()
{
reg(regHelper<testFunc>);
//But this expression is ok.
reg(regHelper<&ctx::map_create>);
std::cout << "this is a test" << std::endl;
}
Here are the error messages when using c++11(gun 10.0.2):
<source>: In function 'int main()':
<source>:30:28: error: no matches converting function 'regHelper' to type 'callBackFunc {aka int (*)(void*)}'
reg(regHelper<testFunc>);
^
<source>:13:5: note: candidate is: template<int (ctx::* func)(void*)> int regHelper(void*)
int regHelper(void*)
^
This is a difference between C++14 and C++17. Simplified:
int f();
template<int (&)()> struct S {};
constexpr auto& q = f;
using R = S<q>; // valid in C++17, invalid in C++14
The change is to Allow constant evaluation for all non-type template arguments, meaning that now a constexpr variable naming a function (member function, etc.) is permissible as an NTTP where previously only the actual name of the function was permitted.

std::function as a (non) const input parameter sent by &

I wonder why std::function<...(...)> & needs to be specified as const when passed as an input parameter to a function. AFAIK there is no way to change it, right? Here is an example that compiles and runs fine. If I remove the const qualifier I get an error:
#include <iostream>
#include <functional>
//int foo(std::function<int(int)> &f)
int foo(const std::function<int(int)> &f)
{
return f(6);
}
int main(int argc, char **argv)
{
auto f1 = [=](int i){ if (i<5) {return 8*2;} else {return 2;} };
auto f2 = [=](int i){ if (i>3) {return i*i;} else {return 2;} };
std::cout << foo(f1) << "\n";
}
When I use the declaration without the const I get the following error:
main.cpp: In function ‘int main(int, char**)’:
main.cpp:13:21: error: cannot bind non-const lvalue reference of type ‘std::function<int(int)>&’ to an rvalue of type ‘std::function<int(int)>’
std::cout << foo(f1) << "\n";
^
In file included from /usr/include/c++/7/functional:58:0,
from main.cpp:2:
/usr/include/c++/7/bits/std_function.h:685:7: note: after user-defined conversion: std::function<_Res(_ArgTypes ...)>::function(_Functor) [with _Functor = main(int, char**)::<lambda(int)>; <template-parameter-2-2> = void; <template-parameter-2-3> = void; _Res = int; _ArgTypes = {int}]
function<_Res(_ArgTypes...)>::
^~~~~~~~~~~~~~~~~~~~~~~~~~~~
main.cpp:4:5: note: initializing argument 1 of ‘int foo(std::function<int(int)>&)’
int foo(std::function<int(int)> &f)
^~~
A lambda is not a std::function, but a std::function can be created from a lambda. When you pass either f1 or f2 to foo(), the compiler must construct a temporary std::function object to give to the f parameter. However, an lvalue reference to a non-const object cannot be bound to a temporary object, exactly as the error message says.
To allow the passing of a temporary object, the f parameter must be changed to take the std::function by either:
value
lvalue reference to a const object
rvalue reference
Otherwise, you have to construct the std::function yourself in a variable, then pass that instead:
#include <iostream>
#include <functional>
//int foo(const std::function<int(int)> &f)
int foo(std::function<int(int)> &f)
{
return f(6);
}
int main(int argc, char **argv)
{
auto f1 = [=](int i){ if (i<5) {return 8*2;} else {return 2;} };
auto f2 = [=](int i){ if (i>3) {return i*i;} else {return 2;} };
std::function<int(int)> f = f1;
std::cout << foo(f) << "\n";
}

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

Class object as template funciton argument

I have an issue with the templates and my knowledge about them is definitely limited.
So I have a class which should store some information:
class Q
{
int integer;
int fractional;
public:
constexpr Q(int i,int f) : integer(i),fractional(f) {}
int get_i() const {return this->integer;}
int get_f() const {return this->fractional;}
constexpr int get_w() {return this->integer + this->fractional;}
friend ostream& operator<<(ostream& os, const Q& q){ os << "Q" << q.integer << "." << q.fractional << " (w:" << q.integer + q.fractional << ")"; return os; }
};
Then I have my templated function. This is just an example but it show the point:
template <Q input_q, Q output_q,const unsigned int X_0_evaluated_bit> void calculate_stuff (const int max_iterations)
{
std::array<Q,input_q.get_w()> input_queue_q;
}
And at the end the main (I'm using the SystemC library) where I generate the constant object Q that I want to use in the function
int sc_main(int argc, char *argv[])
{
constexpr Q Q1_i = Q(1,10);
constexpr Q Q1_o = Q(0,11);
// Number of bits used to address the LUT for the initial value
const unsigned int X_0_evaluated_bit = 5;
// Number of iteration for the Newton-Raphson
const int max_iterations = 2;
calculate_stuff <Q1_i,Q1_o,X_0_evaluated_bit> (max_iterations);
return 0;
}
If I try to compile I get the following error message:
check_ac_one_over.cpp:31:13: error: ‘class Q’ is not a valid type for a template non-type parameter
template <Q input_q, Q output_q,const unsigned int X_0_evaluated_bit> void calculate_stuff (const int max_iterations)
^
check_ac_one_over.cpp:31:24: error: ‘class Q’ is not a valid type for a template non-type parameter
template <Q input_q, Q output_q,const unsigned int X_0_evaluated_bit> void calculate_stuff (const int max_iterations)
^
check_ac_one_over.cpp: In function ‘void calculate_stuff(int)’:
check_ac_one_over.cpp:33:31: error: template argument 2 is invalid
std::array<Q,input_q.get_w()> input_queue_q;
^
check_ac_one_over.cpp:33:46: error: invalid type in declaration before ‘;’ token
std::array<Q,input_q.get_w()> input_queue_q;
^
check_ac_one_over.cpp: In function ‘int sc_main(int, char**)’:
check_ac_one_over.cpp:102:64: error: no matching function for call to ‘calculate_stuff(const int&)’
calculate_stuff <Q1_i,Q1_o,X_0_evaluated_bit> (max_iterations);
^
check_ac_one_over.cpp:102:64: note: candidate is:
check_ac_one_over.cpp:31:76: note: template<<typeprefixerror>input_q, <typeprefixerror>output_q, unsigned int X_0_evaluated_bit> void calculate_stuff(int)
template <Q input_q, Q output_q,const unsigned int X_0_evaluated_bit> void calculate_stuff (const int max_iterations)
^
check_ac_one_over.cpp:31:76: note: template argument deduction/substitution failed:
check_ac_one_over.cpp:102:64: note: invalid template non-type parameter
calculate_stuff <Q1_i,Q1_o,X_0_evaluated_bit> (max_iterations);
^
check_ac_one_over.cpp:102:64: note: invalid template non-type parameter
make: *** [check_ac_one_over.o] Error 1
Now I'm not sure if what I'm trying to do is possible. Does anyone have some ideas how can I make it work ?
Cheers,
Stefano
Does anyone have some ideas how can I make it work ?
Not a great idea, I suppose, but...
The error message is clear:
‘class Q’ is not a valid type for a template non-type parameter
But, in your example, you don't use the full Q object: you use the value of input_q.get_w().
So I suppose that you can pass as template parameter not the full Q object but only the value returned by get_w() that is a int so is a valid as template non-type parameter.
Something (using only the first template parameter; no idea about the use of the other)
template <int input_dim>
void calculate_stuff (const int max_iterations)
{
std::array<Q, input_dim> input_queue_q;
}
that you can call (taking in count that Q1_i and get_w() are constexpr)
calculate_stuff<Q1_i.get_w()> (1);
But observe that
1) get_w() should be also const, not only constexpr
constexpr int get_w() const {return this->integer + this->fractional;}
because a constexpr method isn't automatically const (starting from C++14) but a constexpr object is also const (so can't use get_w() if it isn't defined const)
2) If you want an array of Q as follows
std::array<Q, input_dim> input_queue_q;
the Q type require a constructor without parameters; by example adding default values to your constructor
constexpr Q(int i = 0, int f = 0) : integer(i),fractional(f) {}
I've found a solution. It's not the perfect one but it's quite close. Instead to use the object I'm using the pointer to the object which has become a global variable:
class Q
{
int integer;
int fractional;
public:
constexpr Q(int i = 0,int f = 0) : integer(i),fractional(f) {}
constexpr int get_i() const {return this->integer;}
constexpr int get_f() const {return this->fractional;}
constexpr int get_w() const {return this->integer + this->fractional;}
friend ostream& operator<<(ostream& os, const Q& q){ os << "Q" << q.integer << "." << q.fractional << " (w:" << q.integer + q.fractional << ")"; return os; }
};
template <const Q *input_q,const Q *output_q,const unsigned int X_0_evaluated_bit> void calculate_stuff (const int max_iterations)
{
std::array<Q,input_q->get_w()> input_queue_q;
}
constexpr Q Q1_i = Q(1,10);
constexpr Q Q1_o = Q(0,11);
int sc_main(int argc, char *argv[])
{
// Number of bits used to address the LUT for the initial value
const unsigned int X_0_evaluated_bit = 5;
// Number of iteration for the Newton-Raphson
const int max_iterations = 2;
calculate_stuff <&Q1_i,&Q1_o,X_0_evaluated_bit> (max_iterations);
return 0;
}
Cheers.