c++11 Why doesn't this move constructor work? - c++11

I've written the code posted below. I was hoping to get to move the content of a vector between instances of LargeClass. The move constructor is being used, but instead of moving I get copies only.
Why doesn't the move semantics work as expected here?
Code:
#include <iostream>
#include <vector>
class LargeClass
{
public:
explicit LargeClass (void): numbers(20, 10)
{
}
LargeClass (const LargeClass &rhs): numbers(rhs.numbers)
{
std::cout << "Using LargeClass copy constructor" << '\n';
}
LargeClass (const LargeClass &&rhs): numbers(std::move(rhs.numbers))
{
std::cout << "Using LargeClass move constructor" << '\n';
}
const int* getNumbersAddress(void) const
{
return (numbers.data());
}
private:
std::vector<int> numbers;
};
int main()
{
LargeClass l1;
std::cout << "l1 vector address: " << l1.getNumbersAddress() << '\n';
LargeClass l2(l1);
std::cout << "l1 vector address: " << l1.getNumbersAddress() << '\n';
std::cout << "l2 vector address: " << l2.getNumbersAddress() << '\n';
LargeClass l3 = std::move(l2);
std::cout << "l1 vector address: " << l1.getNumbersAddress() << '\n';
std::cout << "l2 vector address: " << l2.getNumbersAddress() << '\n';
std::cout << "l3 vector address: " << l3.getNumbersAddress() << '\n';
return 0;
}
Possible output:
l1 vector address: 0x18ce010
Using LargeClass copy constructor
l1 vector address: 0x18ce010
l2 vector address: 0x18ce070
Using LargeClass move constructor
l1 vector address: 0x18ce010
l2 vector address: 0x18ce070
l3 vector address: 0x18ce0d0

The rvalue references don't make sense in their const forms because you want to modify them (you want to "move" them ). Objects created as const in C++ are in read-only memory, from which grabbing/modifying internal resources won't be possible.
The syntax for move constructor normally should be
class_name ( class_name && )
So use :
LargeClass ( LargeClass&& rhs )

Although a constructor taking const LargeClass&& is technically known as a move constructor (12.8/3) (which is baffling, but explicitly stated in [C++11: 12.8/3]), it's pretty bloomin' useless; it's self-evident that you cannot move from a const expression, because you cannot modify const things and moving implies altering the source! In particular, you cannot actually move from a const vector, which is why that does not occur here and why you see what you see.
A more useful move constructor looks like this:
LargeClass(LargeClass&& rhs)

Related

How to append more items to an existing vector contained in the value field of a std::map?

I have a std::vector<std::string>>. Following is my full program:
#include <iostream>
#include <vector>
#include <string>
#include <map>
int main() {
std::cout << " -- Beginining of program -- " << std::endl;
std::map<std::string, std::vector<std::string>> my_map_2;
std::vector<std::string> s = {"a", "b", "c"};
my_map_2.insert(std::make_pair("key1", s));
std::vector<std::string> s2 = {"d", "e", "f"};
my_map_2.insert(std::make_pair("key1", s2));
for(auto const &map_item: my_map_2) {
std::cout << map_item.first << " " << map_item.second[0] << std::endl;
std::cout << map_item.first << " " << map_item.second[1] << std::endl;
std::cout << map_item.first << " " << map_item.second[2] << std::endl;
std::cout << map_item.first << " " << map_item.second[3] << std::endl;
std::cout << map_item.first << " " << map_item.second[4] << std::endl;
std::cout << map_item.first << " " << map_item.second[5] << std::endl;
}
std::cout << " -- End of program -- " << std::endl;
return 0;
}
Problem:
I don't see the items of s2 when I print values of my_map_2. I see them only if I add s2 with a new key! If I do my_map_2.insert(std::make_pair("key2", s2)) instead of my_map_2.insert(std::make_pair("key1", s2)), I do see the items.
Question:
So, my question is, how to I append more items to the vector pointed to by key1 of my_map_2?
The below fails because the key is already taken:
std::vector<std::string> s2 = {"d", "e", "f"};
my_map_2.insert(std::make_pair("key1", s2)); // fails
To append to the mapped vector, you could do like this:
auto& vec = my_map_2["key1"]; // get reference to the existing vector
vec.insert(vec.end(), s2.begin(), s2.end()); // append to it
To view the keys and all the values in the vector you could change your loop to this:
for(auto const&[key, value]: my_map_2) {
for(const std::string& str : value) {
std::cout << key << ' ' << str << '\n';
}
}
my_map_2["key1"] is always a valid vector. You can insert into it directly
#include <iostream>
#include <vector>
#include <string>
#include <map>
int main() {
std::cout << " -- Beginining of program -- " << std::endl;
std::map<std::string, std::vector<std::string>> my_map_2;
std::vector<std::string> s = {"a", "b", "c"};
my_map_2["key1"].insert(my_map_2["key1"].end(), s.begin(), s.end());
std::vector<std::string> s2 = {"d", "e", "f"};
my_map_2["key1"].insert(my_map_2["key1"].end(), s2.begin(), s2.end());
for(auto const &map_item: my_map_2) {
for(auto const &value: map_item.second) {
std::cout << map_item.first << " " << value << std::endl;
}
}
std::cout << " -- End of program -- " << std::endl;
return 0;
}
Get iterator to key1, and just pushs back new items to existing vector:
std::vector<std::string> s2 = {"d", "e", "f"};
auto it = my_map_2.find("key1");
if (it != my_map_2.end())
std::move(s2.begin(), s2.end(), std::back_inserter(it->second));
else
my_map_2.insert(std::make_pair("key1",std::move(s2)));
To see: d,e,f you have to access 3,4 and 5 indices of vector. (You want to append new items, or just override existed items for given key?)

c++11 how to check template parameter pack has N args before calling function with N args

Following on from this extracting a template parameter pack with different types into a vector of doubles produces warnings and cigien's answer.
I have the following code:
enum class p_type {p1, p2, p3};
class testerx
{
public:
void process1(double a)
{
std::cout << "1" << a << std::endl;
};
void process2(double a, double b)
{
std::cout << "2" << a << " " << b << std::endl;
};
void process3(double a, double b, double c)
{
std::cout << "3" << a << " " << b << " " << c << std::endl;
};
};
// The template type
template<typename TESTER, typename... ARGS>
class tester_templatex
{
public:
explicit tester_templatex(p_type type) : m_type(type) {};
void process(ARGS... args)
{
// Create a vector to put the args into. use double since that can hold all of the types
// that I am using
size_t param_count = sizeof...(args);
std::cout << "PARAM COUNT X " << param_count << std::endl;
std::vector<double> args_vect = {static_cast<double>(args)...};
for (auto arg : args_vect)
{
std::cout << "arg: " << arg << std::endl;
}
// Now call the tester
std::cout << "running tester: ";
switch (m_type)
{
case p_type::p1:
if constexpr (sizeof...(args) == 1)
m_tester.process1(args...);
break;
case p_type::p2:
if constexpr (sizeof...(args) == 2)
m_tester.process2(args...);
break;
case p_type::p3:
if constexpr (sizeof...(args) == 3)
m_tester.process3(args...);
break;
}
std::cout << std::endl;
};
p_type m_type;
TESTER m_tester;
};
main:
int main() {
tester_templatex<testerx, int> templatex1(p_type::p1);
tester_templatex<testerx, int, double> templatex2(p_type::p2);
tester_templatex<testerx, int, double, int> templatex3(p_type::p3);
templatex1.process(4);
templatex2.process(4, 5.123);
templatex3.process(4, 5.123, 6);
return 0;
}
Here I have test class with 3 different functions. I have a template class which picks the function to call based on the p_type (bad name - dont ask!).
This works for c++17 compiled code. But I only have c++11 where I need to run this code. c++11 does not support if constexpr:
case p_type::p3:
if constexpr (sizeof...(args) == 3)
m_tester.process3(args...);
break;
Without the if constexpr I get errors that the m_tester.process1/2/3 functions that don't match the parameter pack because they don't have the right number of parameters.
How can I fix this for c++11? - is it possible with a similar method?
Is there another way to extract N arguments from a parameter pack in c++11? - or some sort of type traits check?
For each of your functions have an overload that does nothing:
template<typename... ARGS>
void process3(ARGS&...) { }
and then just call the function without testing for the size of the pack:
case p_type::p3:
m_tester.process3(args...);
break;
This should pick the non-templated function when there are suitably many arguments, and the function template in other cases.

How to mix auto with brace-initialization to return a std::pair properly?

I have this example which is a function that returns a std::pair which holds a string value and its size.
std::pair<std::string, int> getLastPair(const vector<string>& vec) {
return{ vec.back(), vec.back().size() };
}
int main(){
vector<string> names{ "An", "Apple", "A", "Day", "Keeps", "The",
"Doctor", "Away"};
auto p{getLastPair(names)}; // ok p is a pair<string, int>
std::cout << p.first << " " << p.second << std::endl;
std::cout << typeid(p).name() << std::endl;
auto p2 = { getLastPair(names) }; // why it is not a pair but an initializer list?
std::cout << typeid(p2).name() << std::endl; // initializer list
std::pair<std::string, int> p3 = { getLastPair(names) }; // ok a pair
std::cout << p3.first << " : " << p3.second << std::endl;
decltype (getLastPair(names)) p4 = { getLastPair(names) }; // ok
std::cout << p4.first << " : " << p4.second << std::endl; // ok
}
Why the first initialization returns a pair as expected but the second one p2 is not a pair but an initializer_list instead?
As you can see p3 works fine (returns a pair) aslong as I've provided the type explicitly!?
Is the problem in Type Specifier auto?
Also p4 works fine with decltype.

Which one to choose between pointer way and non pointer way?

#include <iostream>
class A{
public:
A(){std::cout << "basic constructor called \n";};
A(const A& other) {
val = other.x
std::cout << "copy constructor is called \n";
}
A& operator=(const A& other){
val = other.x
std::cout << "\n\nassignment operator " << other.val << "\n\n";
}
~A(){
std::cout << "destructor of value " << val <<" called !!\n";
}
A(int x){
val = x;
std::cout << " A("<<x<<") constructor called \n";
}
int get_val(){
return val;
}
private:
int val;
};
int main(){
// non pointer way
A a;
a = A(1);
std::cout << a.get_val() << std::endl;
a = A(2);
std::cout << a.get_val() << std::endl;
// pointer way
A* ap;
ap = new A(13);
std::cout << ap->get_val() << std::endl;
delete ap;
ap = new A(232);
std::cout << ap->get_val() << std::endl;
delete ap;
return 0;
}
I initially create an object out of default constructor and then assign tmp r-value objects A(x) to a. This ends up calling assignment operator. So in this approach there are 3 steps involved
(non-pointer way)
1) constructor
2) assignment operator
3) destructor
Where as when I use pointer it only requires two step
(pointer way)
1) constructor
2) destructor
My question is should I use non-pointer way to create new classes or should I use pointer way. Because I have been said that I should avoid pointers (I know I could also use shared_ptr here).
Rule of thumb: Prefer to create objects on the stack. There is less work for memory management when you create objects on the stack. It is also more efficient.
When do you have to create objects on the heap?
Here are some situations that need it:
You need to create an array of objects where the size of the array is known only at run time.
You need an object to live beyond the function in which it was constructed.
You need to store and/or pass around a pointer to a base class type but the pointer points to a derived class object. In this case, the derived class object, most likely, will need to be created using heap memory.

Address of an instance emplaced to std::vector is invalid

I have 2 std::vectors:
to first vector, I emplace instance
to second vector, I want to store the address of the instance just emplaced
But it does not work, i.e., the stored address differs from the emplaced instance's address.
If it matters at all, I'm on Linux and using g++ 5.1 and clang 3.6 with -std=c++11.
Here's a working example to illustrate the problem.
#include <iostream>
#include <vector>
struct Foo {
Foo(int a1, int a2) : f1(a1), f2(a2) {}
int f1;
int f2;
};
int main(int, char**) {
std::vector<Foo> vec1;
std::vector<Foo*> vec2;
int num = 10;
for (int i = 0; i < num; ++i) {
vec1.emplace_back(i, i * i);
// I want to store the address of *emplaced* instance...
vec2.push_back(&vec1.back());
}
// same
std::cout << "size 1: " << vec1.size() << std::endl;
std::cout << "size 2: " << vec2.size() << std::endl;
// same for me
std::cout << "back 1: " << &vec1.back() << std::endl;
std::cout << "back 2: " << vec2.back() << std::endl;
// typically differ ?
std::cout << "front 1: " << &vec1.front() << std::endl;
std::cout << "front 2: " << vec2.front() << std::endl;
for (int i = 0; i < num; ++i) {
std::cout << i + 1 << "th" << std::endl;
// same for last several (size % 4) for me
std::cout << "1: " << &vec1[i] << std::endl;
std::cout << "2: " << vec2[i] << std::endl;
}
}
Questions
Is it correct behavior ? I guess it's caused by storing the address of temporary instance but I want to know whether it's permitted by the standard (just curious).
If above is true, how to work around this ? I resolved this by changing first one to vector<unique_ptr<Foo>> but is there any idiomatic way ?
Two options:
1) You can simply fix your test. You just need in you test preallocate enough memory first with
vec1.reserve(10);
Well, this is implementation details for std::vector. As more and more items are added to std::vector it needs to get more space for them. And this space must be contigious. So when there is not enough space for a new element std::vector allocates a bigger block of memory, copies existing elements to it, add the new element and finally frees the block of memory that it used before. As a result addresses that you stored in vec2 might become invalid.
However, if you preallocate enough memory for 10 elements then you code is correct.
Or, since reserving memory is sort of tricky thing to do
2) use std::deque since insertion and deletion at either end of a deque never invalidates pointers or references to the rest of the elements (http://en.cppreference.com/w/cpp/container/deque) and forget about the problem with invalidated addresses. So no need to reserve memory.

Resources