c++, what happens when an lvalue is passed to T&&? - c++

#include <bits/stdc++.h>
#include <vector>
template <typename T>
void g(T&& val)
{
std::vector<T> v;
}
int main()
{
// g(2);
int i;
g(i);
}
When g(2) is called, it complies, but when g(i) is called, the complier has a lot of errors.
Some of the errors are pasted as follows:
forming pointer to reference type ‘int&’
no members matching ‘std::vector<int&, std::allocator<int&> >::_Base {aka std::_Vector_base<int&, std::allocator<int&> >}::_M_allocate’ in ‘std::vector<int&, std::allocator<int&> >::_Base’ {aka ‘struct std::_Vector_base<int&, std::allocator<int&> >’}
no members matching ‘__gnu_cxx::__alloc_traits<std::allocator<int&>, int&>::_Base_type {aka std::allocator_traits<std::allocator<int&> >}::allocate’ in ‘__gnu_cxx::__alloc_traits<std::allocator<int&>, int&>::_Base_type’ {aka ‘struct std::allocator_traits<std::allocator<int&> >’}
forming pointer to reference type ‘int&’
my question:
1. In this case, when the literal value 2 is called, what type is derived for T ?
2. Why doesn't it complie when on i ? How to understand these error messages ?

1: A forwarding reference, and
g(2) makes T an int
g(i) makes T an int&
2: You can't have arrays of references (new T&[x]). You could use std::remove_cvref_t to get the type int out of T:
#include <type_traits>
template <typename T>
void g(T&& val) {
using type = std::remove_cvref_t<T>; // type is int
std::vector<type> v;
}

Your universal reference resolves to int for g(2) and int& for g(i)
You can't have a std::vector of a reference type (see this explanation).

Related

Why can't std::pair construct from function type that is passing by reference?

I read from somewhere that constructor of std::pair is passing by value(before c++11) because if it's passing by reference, std::pair can't construct from reference function type. why?
//https://godbolt.org/z/5dMaG8E11
#include <iostream>
#include <utility>
void fun();
template <typename T>
void use(const T &)
{}
template <typename T, typename U>
std::pair<T, U> mp(const T & t, const U & u)
{
return std::pair(t, u);
}
int main()
{
use(::mp(fun, 3)); // failed
use(std::make_pair(fun, 3)); // ok
}
/opt/compiler-explorer/gcc-10.3.0/lib/gcc/x86_64-linux-gnu/10.3.0/../../../../include/c++/10.3.0/bits/stl_pair.h:217:11: error: data member instantiated with function type 'void ()'
_T1 first; ///< The first member
^
<source>:18:9: note: in instantiation of template class 'std::pair<void (), int>' requested here
use(::mp(fun, 3)); // failed
^
<source>:13:12: error: no viable conversion from returned value of type 'pair<void (*)(), [...]>' to function return type 'pair<void (), [...]>'
return std::pair(t, u);
^~~~~~~~~~~~~~~
<source>:18:11: note: in instantiation of function template specialization 'mp<void (), int>' requested here
use(::mp(fun, 3)); // failed
^

Allocator-related error when passing lvalue to a universal reference parameter that is used as template parameter to a temporary std container

I have stripped down my use case to a minimal meaningless example:
#include <vector>
template<typename Container>
void f(Container&) { // using && gives compilation error... (*)
std::vector<Container>{};
};
int main() {
std::vector<int> v;
f(v); // (*) ... which is "solved" by std::move-ing v here. Why?
}
which compiles. However, when I add another & to make the unnamed parameter a forwarding reference, I get the error below (compiling with g++ -std=c++11 thatfile.cpp or -std=c++17). On the other hand, std::moveing v at the call site, solves the problem.
I'd like to understand why.
In file included from /usr/include/c++/10.2.0/x86_64-pc-linux-gnu/bits/c++allocator.h:33,
from /usr/include/c++/10.2.0/bits/allocator.h:46,
from /usr/include/c++/10.2.0/vector:64,
from prova.cpp:1:
/usr/include/c++/10.2.0/ext/new_allocator.h: In instantiation of ‘class __gnu_cxx::new_allocator<std::vector<int>&>’:
/usr/include/c++/10.2.0/bits/allocator.h:116:11: required from ‘class std::allocator<std::vector<int>&>’
/usr/include/c++/10.2.0/bits/stl_vector.h:87:21: required from ‘struct std::_Vector_base<std::vector<int>&, std::allocator<std::vector<int>&> >’
/usr/include/c++/10.2.0/bits/stl_vector.h:389:11: required from ‘class std::vector<std::vector<int>&, std::allocator<std::vector<int>&> >’
prova.cpp:5:5: required from ‘void f(Container&&) [with Container = std::vector<int>&]’
prova.cpp:10:8: required from here
/usr/include/c++/10.2.0/ext/new_allocator.h:62:26: error: forming pointer to reference type ‘std::vector<int>&’
62 | typedef _Tp* pointer;
| ^~~~~~~
/usr/include/c++/10.2.0/ext/new_allocator.h:63:26: error: forming pointer to reference type ‘std::vector<int>&’
63 | typedef const _Tp* const_pointer;
| ^~~~~~~~~~~~~
/usr/include/c++/10.2.0/ext/new_allocator.h:103:7: error: forming pointer to reference type ‘std::vector<int>&’
103 | allocate(size_type __n, const void* = static_cast<const void*>(0))
| ^~~~~~~~
/usr/include/c++/10.2.0/ext/new_allocator.h:120:7: error: forming pointer to reference type ‘std::vector<int>&’
120 | deallocate(_Tp* __p, size_type __t)
| ^~~~~~~~~~
...
When you have
template<typename Container>
void f(Container&) { // using && gives compilation error... (*)
std::vector<Container>{};
};
Constainer is deduced as a std::vector and all is well. When you use
template<typename Container>
void f(Container&&) { // using && gives compilation error... (*)
std::vector<Container>{};
};
and you don't use std::move, then Container is deduced as std::vector&, which is a reference type, and you cannot create vectors of a reference type, so you get an error.
When you use std::move(v), then you pass a std::vector&& to the function so Container again gets deduced to std::vector and the code compiles as it is not a reference type.

Custom class pointer as unordered map key

I'm trying to implement unordered_map with pointer to custom class as key and integer as value.
I thought pointer is just an address, so I wouldn't have to create comparison template for unordered_map, since map would compare between addresses. But I get compile error.
My code is as follow for simple testing. Can anyone help me to fix what have I done wrong?
#include <cstdlib>
#include <unordered_map>
#include <iostream>
using namespace std;
class MyClass{
public:
MyClass(int id){m_id = id;};
void PrintThis(){cout << " This is test " << endl;};
int m_id;
};
class Test{
public:
unordered_map<MyClass* mc, int test> mapTest;
};
int main(){
MyClass* mc1 = new MyClass(1);
MyClass* mc2 = new MyClass(2);
Test* tt1 = new Test();
tt1->mapTest.insert(make_pair<MyClass*, int>(mc1, 10));
tt1->mapTest.insert(make_pair<MyClass*, int>(mc2, 20));
auto search = tt1->find(mc1);
if(search != tt1->end()) {
search->first->PrintThis();
}else{
cout << "not Found " << endl;
}
}
Error message is as follow
./main.cpp:17:44: error: wrong number of template arguments (1, should be 5)
unordered_map<MyClass* mc, int test> mapTest;
^
In file included from /usr/include/c++/4.8/unordered_map:48:0,
from ./main.cpp:2:
/usr/include/c++/4.8/bits/unordered_map.h:97:11: error: provided for 'template<class _Key, class _Tp, class _Hash, class _Pred, class _Alloc> class std::unordered_map'
class unordered_map : __check_copy_constructible<_Alloc>
^
./main.cpp: In function 'int main()':
./main.cpp:26:18: error: request for member 'insert' in 'tt1->Test::mapTest', which is of non-class type 'int'
tt1->mapTest.insert(make_pair<MyClass*, int>(mc1, 10));
I think I can manage line 26 error, if line 17 gets fixed...
Thanks in advance!
I tried your code and found 3 problems:
Declaration of map: it should read std::unordered_map<MyClass*, int>
call of undefined functions (tt1->find/tt1->end, should read tt1->testMap.XXX)
Call of make_pair doesn't require template arguments. The compiler will infer them. This actually causes a problem, as the compiler tries to call make_pair(MyClass *&&, int &&). If I omit the template arguments, it works (make_pair(mc1, 10))
As for point 3:
make_pair is declared as follows in C++11 (C++14 just adds constexpr) (cppreference):
template< class T1, class T2 >
std::pair<V1,V2> make_pair( T1&& t, T2&& u );
For template argument deduction, the follwing rule applies (cf. cppreference)
4) If P is an rvalue reference to a cv-unqualified template parameter (so-called "forwarding reference"), and the corresponding function call argument is an lvalue, the type lvalue reference to A is used in place of A for deduction (Note: this is the basis for the action of std::forward)
(emphasis mine)
So the compiler will infer:
std::make_pair<MyClass *&, int>(MyClass *&, int &&);
where MyClass *& can bind to your actual argument.
If you directly specify the template types, the compiler will stick to
std::make_pair<MyClass *, int>(MyClass *&&, int &&).
As your argument is a lvalue, it cannot be converted to a rvalue-reference, and compilation fails
Your declaration of unordered_map<MyClass* mc, int test> mapTest; is invalid syntax. It should be unordered_map<MyClass*, int> mapTest.
Also remove the template parameters from your make_pair calls and change tt1->find to ttl->mapTest.find() and ttl->end() to ttl->mapTest.end().

C++11 decltype: How to declare the type that a pointer points to?

I have the following code:
#include <memory>
int main()
{
int* a = new int(2);
std::unique_ptr<decltype(*a)> p(a);
}
which leads to these error message:
In file included from a.cpp:1:
In file included from /usr/bin/../lib64/gcc/x86_64-unknown-linux-gnu/4.9.2/../../../../include/c++/4.9.2/memory:81:
/usr/bin/../lib64/gcc/x86_64-unknown-linux-gnu/4.9.2/../../../../include/c++/4.9.2/bits/unique_ptr.h:138:14: error: '__test' declared as a pointer to a reference of type 'int &'
static _Tp* __test(...);
^
/usr/bin/../lib64/gcc/x86_64-unknown-linux-gnu/4.9.2/../../../../include/c++/4.9.2/bits/unique_ptr.h:146:35: note: in instantiation of member class 'std::unique_ptr<int &,
std::default_delete<int &> >::_Pointer' requested here
typedef std::tuple<typename _Pointer::type, _Dp> __tuple_type;
^
a.cpp:7:35: note: in instantiation of template class 'std::unique_ptr<int &, std::default_delete<int &> >' requested here
std::unique_ptr<decltype(*a)> p(a);
^
In file included from a.cpp:1:
In file included from /usr/bin/../lib64/gcc/x86_64-unknown-linux-gnu/4.9.2/../../../../include/c++/4.9.2/memory:81:
/usr/bin/../lib64/gcc/x86_64-unknown-linux-gnu/4.9.2/../../../../include/c++/4.9.2/bits/unique_ptr.h:227:33: error: 'type name' declared as a pointer to a reference of type 'int &'
is_convertible<_Up*, _Tp*>, is_same<_Dp, default_delete<_Tp>>>>
^
a.cpp:7:35: note: in instantiation of template class 'std::unique_ptr<int &, std::default_delete<int &> >' requested here
std::unique_ptr<decltype(*a)> p(a);
^
2 errors generated.
I understand the reason is that the unique_ptr template expects type int, but decltype(*a) gives int&. In the case that int is a very long and complicated type, how can I make this code work with decltype?
Use std::decay_t. This is the conversion that is applied when you pass an argument to a function by value.
You can use a typedef inside a templated class and then use template specialisation, like this
template<typename T> struct unref {
typedef T raw;
};
template<typename T> struct unref<T&> {
typedef T raw;
};
int main() {
int* a = new int(2);
std::unique_ptr<unref<decltype(*a)>::raw> p(a);
}

How to use bind1st and bind2nd?

I would like to learn how to use binding functions.
Here is the idea:
I have this function which takes to parameters:
void print_i(int t, std::string separator)
{
std::cout << t << separator;
}
And I would like to do:
std::vector<int> elements;
// ...
for_each(elements.begin(), elements.end(), std::bind2nd(print_i, '\n'));
But it does not work !
Here is what I get:
/usr/include/c++/4.3/backward/binders.h: In instantiation of ‘std::binder2nd<void ()(int, std::string)>’:
main.cpp:72: instantiated from here
/usr/include/c++/4.3/backward/binders.h:138: error: ‘void ()(int, std::string)’ is not a class, struct, or union type
/usr/include/c++/4.3/backward/binders.h:141: error: ‘void ()(int, std::string)’ is not a class, struct, or union type
/usr/include/c++/4.3/backward/binders.h:145: error: ‘void ()(int, std::string)’ is not a class, struct, or union type
/usr/include/c++/4.3/backward/binders.h:149: error: ‘void ()(int, std::string)’ is not a class, struct, or union type
/usr/include/c++/4.3/backward/binders.h:155: error: ‘void ()(int, std::string)’ is not a class, struct, or union type
/usr/include/c++/4.3/backward/binders.h:140: error: field ‘std::binder2nd<void ()(int, std::string)>::op’ invalidly declared function type
/usr/include/c++/4.3/backward/binders.h: In function ‘std::binder2nd<_Operation> std::bind2nd(const _Operation&, const _Tp&) [with _Operation = void ()(int, std::string), _Tp = char]’:
main.cpp:72: instantiated from here
/usr/include/c++/4.3/backward/binders.h:164: error: ‘void ()(int, std::string)’ is not a class, struct, or union type
/usr/include/c++/4.3/bits/stl_algo.h: In function ‘_Funct std::for_each(_IIter, _IIter, _Funct) [with _IIter = __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, _Funct = std::binder2nd<void ()(int, std::string)>]’:
main.cpp:72: instantiated from here
/usr/include/c++/4.3/bits/stl_algo.h:3791: error: no match for call to ‘(std::binder2nd<void ()(int, std::string)>) (int&)’
make: *** [all] Error 1
I could use functor, but it is quicker to use binding.
Thanks!
You need to use a Copyable/Refrencable object, the following works:
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <functional>
void print_i(int t, std::string separator)
{
std::cout << t << separator;
}
int main()
{
std::vector<int> elements;
std::string delim = "\n";
for_each(elements.begin(),
elements.end(),
std::bind2nd(std::ptr_fun(&print_i),delim));
return 0;
}
Normally you can get the same effect by simply doing the following:
#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
int main()
{
std::vector<int> elements;
std::copy(elements.begin(),
elements.end(),
std::ostream_iterator<int>(std::cout,"\n"));
return 0;
}
Also assuming you have access to TR1 in the STL you're using, its always best to revise/replace any uses of bind1st and bind2nd with std::bind
The argument to bind2nd must be an AdaptableBinaryFunction. A plain binary function does not fulfill this requirement (an adaptable function required typedefs for its return and argument types, a plain function type does not provide any typedefs). You could use std::bind which is probably the better choice anyway.
You need to do the following steps:
1. create a struct (or class) that inherits from std::binary_function
2. define your predicate function in the operator() member function of the struct created in step 1
3. use bind2nd to bind an appropriate value to the struct created in step 1
I have done all this in an example. You can read the article and download the complete code on the following link: bind and find
These functions are deprecated since C++11 and removed in C++17. As mentioned in one comment above, the better solution now is to use std::bind and the placeholders:
void callable(int a, int b);
auto f = std::bind1st(&callable, 42); // returns a 1-arg function
becomes:
// returns a 1-arg function
auto f = std::bind(&callable, 42, std::placeholders::_1);