How reference deduce works? - c++

May be duplicated to this.
I read Effective Modern C++. Under Item 1, I found a case for universal reference:
For the last example f(27); I did a test under VS2013.
void process(int& x)
{
std::cout << "int&" << std::endl;
}
void process(int&& x)
{
std::cout << "int&&" << std::endl;
}
template<typename T>
void f(T&& param)
{
std::cout << "------------------------------------------------" << std::endl;
if (std::is_lvalue_reference<T>::value)
{
std::cout << "T is lvalue reference" << std::endl;
}
else if (std::is_rvalue_reference<T>::value)
{
std::cout << "T is rvalue reference" << std::endl;
}
else
{
std::cout << "T is NOT lvalue reference" << std::endl;
}
std::cout << "param is: " << typeid(param).name() << std::endl;
process(std::forward<T>(param));
process(param);
}
int getINT()
{
int x = 10;
return x;
}
int _tmain(int argc, _TCHAR* argv[])
{
f(10);
f(getINT());
return 0;
}
Here is the output:
------------------------------------------------
T is NOT lvalue reference
param is: int
int&&
int&
------------------------------------------------
T is NOT lvalue reference
param is: int
int&&
int&
I found that within the template function, without std::forward<T>(param), process(int& x) will be called but according to the book, the type for param should be rvalue reference, so process(int&& x) should be called. But this is not the case. Is it I misunderstand something?
Here is the forwarding reference I found from other thread:

You're confusing types with value categories. As a named parameter, param is an lvalue, then for process(param); process(int& x) will be called.
That's why we should use std::forward with forwarding reference; std::forward<T>(param) converts param to rvalue for this case, then process(int&& x) will be called (as expected).

Related

Missing automatic move of returned local variable

I'm running into an issue where under a specific set of conditions, a local variable being returning by a function is copied instead of moved. So far, it seems like it needs to meet the following:
The returned variable has some usage in the function. I'm assuming otherwise the whole copy/move is ellided.
The returned type is using a perfect forwarding-style constructor. From this answer (Usage of std::forward vs std::move) I learned that this style of constructor has some different deduction rules.
Be compiled in gcc before 8.0. Compiling under clang (and apparently gcc 8.0+, thanks PaulMcKenzie for the comment) produces the results I expect, however I'm not free to change the compiler being used in the larger project.
Here's some minimum reproduction:
#include <iostream>
#include <type_traits>
// Test class to print copies vs. moves.
class Value
{
public:
Value() : x(0) {}
Value(Value&& other) : x(other.x)
{
std::cout << "value move" << std::endl;
}
Value(const Value& other) : x(other.x)
{
std::cout << "value copy" << std::endl;
}
int x;
};
// A container class using a separate lvalue and rvalue conversion constructor.
template<typename T>
class A
{
public:
A(const T& v) : data_(v)
{
std::cout << "lvalue conversion" << std::endl;
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
A(T&& v) : data_(std::move(v))
{
std::cout << "rvalue conversion" << std::endl;
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
T data_;
};
// A container class using a single perfect forwarding constructor.
template<typename T>
class B
{
public:
template <typename U>
B(U&& v) : data_(std::forward<U>(v))
{
std::cout << "template conversion" << std::endl;
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
T data_;
};
// Get a Value rvalue.
Value get_v()
{
Value v;
v.x = 10; // Without this things get ellided.
return v;
}
// Get an A<Value> rvalue.
A<Value> get_a()
{
Value v;
v.x = 10; // Without this things get ellided.
return v;
}
// Get a B<Value> rvalue.
B<Value> get_b()
{
Value v;
v.x = 10; // Without this things get ellided.
return v;
}
int main()
{
Value v = Value();
std::cout << "--------\nA" << std::endl;
std::cout << "lvalue" << std::endl;
A<Value> a0(v);
std::cout << a0.data_.x << std::endl;
std::cout << "rvalue" << std::endl;
A<Value> a1(get_v());
std::cout << a1.data_.x << std::endl;
std::cout << "get_a()" << std::endl;
std::cout << get_a().data_.x << std::endl;
std::cout << "--------\nB" << std::endl;
std::cout << "lvalue" << std::endl;
B<Value> b0(v);
std::cout << b0.data_.x << std::endl;
std::cout << "rvalue" << std::endl;
B<Value> b1(get_v());
std::cout << b1.data_.x << std::endl;
std::cout << "get_b()" << std::endl;
std::cout << get_b().data_.x << std::endl;
return 0;
}
Under gcc this produces:
--------
A
lvalue
value copy
lvalue conversion
A<T>::A(const T&) [with T = Value]
0
rvalue
value move
rvalue conversion
A<T>::A(T&&) [with T = Value]
10
get_a()
value move <---- Works with separate constructors.
rvalue conversion
A<T>::A(T&&) [with T = Value]
10
--------
B
lvalue
value copy
template conversion
B<T>::B(U&&) [with U = Value&; T = Value]
0
rvalue
value move
template conversion
B<T>::B(U&&) [with U = Value; T = Value]
10
get_b()
value copy <---- Not what I expect!
template conversion
B<T>::B(U&&) [with U = Value&; T = Value]
10
For completeness, clang gives:
--------
A
lvalue
value copy
lvalue conversion
A<Value>::A(const T &) [T = Value]
0
rvalue
value move
rvalue conversion
A<Value>::A(T &&) [T = Value]
10
get_a()
value move
rvalue conversion
A<Value>::A(T &&) [T = Value]
10
--------
B
lvalue
value copy
template conversion
B<Value>::B(U &&) [T = Value, U = Value &]
0
rvalue
value move
template conversion
B<Value>::B(U &&) [T = Value, U = Value]
10
get_b()
value move <---- Like this!
template conversion
B<Value>::B(U &&) [T = Value, U = Value]
10
I have two questions:
Is this allowed behavior by gcc?
Is there any way to force move behavior here through changing the implementation of A/B? It seems any time you have a templated function parameter being taken as && it will trigger the special rules for perfect forwarding, so if I try to provide two constructors as in the A example, one taking const U& and one taking U&&, it won't avoid the problem as long as they have other templating in place.

C++ function overloading char called instead of double

I have a bunch of overloaded functions to take on specific int sizes, float, double, char and std::string.
eg:
#include <cstdint>
#include <iostream>
void some_func(uint8_t& src) {
std::cout << "inside uint8_t" << std::endl;
}
void some_func(uint16_t& src) {
std::cout << "inside uint16_t" << std::endl;
}
void some_func(uint32_t& src) {
std::cout << "inside uint32_t" << std::endl;
}
void some_func(uint64_t& src) {
std::cout << "inside uint64_t" << std::endl;
}
void some_func(bool& src) {
std::cout << "inside bool" << std::endl;
}
void some_func(double& src) {
std::cout << "inside double" << std::endl;
}
void some_func(float& src) {
std::cout << "inside float" << std::endl;
}
void some_func(const char& src) {
std::cout << "inside char" << std::endl;
}
int main() {
some_func((bool)true);
some_func((double)13.4);
return 0;
}
For some reason the overloaded const char& src function is called instead of double and bool. They both have explicit function overloads written, so why is the compiler promoting? moreover double is 64bits, so how is that being coverted to char? Can I solve this with template specialization? (I need to perform specifc actions depending on the data type)
Note: I am on Visual studio 2019 compiling with std=c++17
Non-const lvalue references can't be initialized with rvalues. (bool)true; and (double)3.14 are rvalues.
The only overload with a const lvalue reference parameter (which can be initialized with rvalues) is the const char& src one, that's why it's used.
Since the type is different (char vs bool/double), a temporary char is created from the argument, and then is bound to the reference.

How to get value type by C++ overload?

I'm confused about how to tell apart "lvalue" and "rvalue" in C, and "lvalue", "xvalue", "prvalue" in C++.
I'm thinking about getting the value type by overload in C++.
#include <iostream>
template<typename T>
void value_category(T & var)
{
std::cout << "Left Value" << std::endl;
}
template<typename T>
void value_category(T && var)
{
std::cout << "Right Value" << std::endl;
}
int main(void)
{
int arr1[4];
int arr2[4];
int arr3[4];
int * mat[3] = {arr1, arr2, arr3};
value_category(arr3);
value_category(arr2[3]);
value_category(mat);
value_category(mat[0]);
}
But it only tells whether lvalue or rvalue? Are there other solutions?

Simple version of `std::function`: lifetime of function object?

I tried to build a very simple version of std::function. Below the code of a very first version. My question is about the lifetime of the temporary object from the lambda-expression, because I'm actually storing a reference to it. Or is the lifetime of the object prolonged?
It tried to use a copy of the function (T mf instead of const T& mf inside struct F), but that gives an error due to the decaying of the function to a pointer.
#include <iostream>
template<typename T>
struct F {
F(const T& f) : mf{f} {
std::cout << __PRETTY_FUNCTION__ << '\n';
}
void test() {
std::cout << __PRETTY_FUNCTION__ << '\n';
mf();
}
const T& mf;
};
template<typename T>
struct F<T*> {
F(T f) : mf{f} {
std::cout << __PRETTY_FUNCTION__ << '\n';
}
void test() {
std::cout << __PRETTY_FUNCTION__ << '\n';
mf();
}
T* mf;
};
void g() {
std::cout << __PRETTY_FUNCTION__ << '\n';
}
int main() {
F<void(void)> f1(g);
f1.test();
auto g1 = g;
F f2(g1);
f2.test();
F f3([](){ // lifetime?
std::cout << __PRETTY_FUNCTION__ << '\n';
});
f3.test();
auto l1 = [](){
std::cout << __PRETTY_FUNCTION__ << '\n';
};
F f4(l1);
f4.test();
}
You do have lifetime problems here: lifetime extension thanks to const only applies for local const references.
You need to make sure that the referenced function lives at least as long as the wrapper, or you need to copy/move the function into the wrapper.
but that gives an error due to the decaying of the function to a pointer
You can use std::decay_t<T> to ensure that you're copying/moving objects (e.g. closures) into the wrapper.

How to distiguish between an rvalue and rvalue reference in a function parameter

When passed a parameter, I would like to distinguish between these two cases in a function parameter, like this:
int rvalue();
int&& rvalue_ref();
f(rvalue());
f(rvalue_ref());
However, when I try with forwarding references like this:
int rvalue()
{
return 1;
}
int&& rvalue_ref(int i)
{
return std::move(i);
}
template<class T>
void f(T&& x)
{
if (std::is_rvalue_reference<T>())
{
std::cout << "Rvalue reference" << std::endl;
}
else if (std::is_lvalue_reference<T>())
{
std::cout << "Lvalue reference" << std::endl;
}
else
{
std::cout << "Not a reference" << std::endl;
}
}
int main()
{
f(rvalue()); // Should print "Not a reference"
f(rvalue_ref(1)); // Should print "Rvalue reference"
}
It prints out "Not a reference" for both cases. Is there a way to distinguish both cases in C++?
I don't see how to do this solely using function parameter. The distinction between xvalue and prvalue may be lost in the function call.
But you can do it with a macro that calls decltype on the argument, before calling the function. Here is an example that calls your function with the relevant information as a second parameter. I borrowed code from this thread.
#include <iostream>
int rvalue()
{
return 1;
}
int&& rvalue_ref(int &&i) // Modified signature to avoid return reference to local variable (ty. user657267)
{
return std::move(i);
}
template<typename T>
struct value_category {
// Or can be an integral or enum value
static constexpr auto value = "prvalue";
};
template<typename T>
struct value_category<T&> {
static constexpr auto value = "lvalue";
};
template<typename T>
struct value_category<T&&> {
static constexpr auto value = "xvalue";
};
// Double parens for ensuring we inspect an expression,
// not an entity
#define VALUE_CATEGORY(expr) value_category<decltype((expr))>::value
#define f(X) f_(X, VALUE_CATEGORY(X))
template<class T>
void f_(T&& x, char const *s)
{
std::cout << s << '\n';
}
int main()
{
f(rvalue()); // Should print "Not a reference"
f(rvalue_ref(1)); // Should print "Rvalue reference"
int j; f(j);
}
Output:
prvalue
xvalue
lvalue
Of course you can trivially modify the strings to suit, or replace them with enums etc.
The reason why your code said 'Not a reference' is that you passed T to std::std::is_lvalue_reference<> and std::std::is_rvalue_reference<>.
You should use decltype() to get its original type.
template<class T>
void f( T&& x )
{
if ( std::is_lvalue_reference< decltype( x ) >() )
{
std::cout << "Lvalue reference" << std::endl;
}
else if ( std::is_rvalue_reference < decltype( x ) >() )
{
std::cout << "Rvalue reference" << std::endl;
}
else
{
std::cout << "Not a reference" << std::endl;
}
}