c++ std transform calls copy constructor multiple times - algorithm

During implementation of a code copying of map elements into a vector I found out that std::transform calls the copy ctor of my custom class multiple times despite of prior vector size reservation. So I compared different ways to do this, and saw that the best method is a simple for-loop. So my question is: are these algorithms really more expensive? If so what is the advantage of them? Or does it not matter for small number of elements? Here my test code:
class A
{
public:
A() {}
A(int ai) : i(ai) {}
A(A& a) : i(a.i) { std::cout << "copy ctor " << i << std::endl; }
A(const A& a) : i(a.i) { std::cout << "copy const ctor " << i << std::endl; }
A(A&& a) : i(std::move(a.i)) { std::cout << "move ctor " << i << std::endl; }
// I removed overloaded operators because they are not called here...
private:
int i;
};
std::map<int, A> m;
m.insert(std::make_pair(0, A(7)));
m.insert(std::make_pair(1, A(3)));
m.insert(std::make_pair(2, A(1)));
std::vector<A> vec;
vec.reserve(m.size());
std::transform(m.begin(), m.end(), std::back_inserter(vec), [](const std::pair<int, A> & p) { return p.second; }); // 3 calls for each ctor
//... clean up and reserve vec
std::for_each(m.begin(), m.end(), [&vec](const std::pair<int, A> & p) { vec.push_back(p.second); }); // 2 calls for each ctor
//... clean up and reserve vec
for (const auto & p : m) // 1 call for each ctor
{
vec.emplace_back(p.second);
}

The element type in a std::map is std::pair<const Key, Value>.
In your example use use std::pair<int, A> without const, so you need to copy the pair. This adds a copy in the first two solutions. If you change it to std::pair<const int, A> you can see 2 constructor calls in the first iteration and only one in the second (on par with your for loop).
For the first iteration you now have 1 copy and one move. This is because your lambda needs to copy the A then back_inserter move constructs it into the vector. Normally I wouldn't worry about an extra move since they are supposed to be cheap.
However, you can get rid of it. We can force the lamba to not copy the A instance, by ,for example, specifying the return type to be a reference. So now the back_inserter will have to copy when it inserts, but that's it.
This version of your code does one copy per element for each solution:
std::map<int, A> m; m.insert(std::make_pair(0, A(7))); m.insert(std::make_pair(1, A(3))); m.insert(std::make_pair(2, A(1))); std::vector<A> vec;
std::cout << "Transform" << std::endl;
vec.reserve(m.size());
std::transform(m.begin(), m.end(), std::back_inserter(vec),
// changed the key type and return type.
[](const std::pair<const int, A> & p)->const A& { return p.second; });
std::cout << "for_each" << std::endl;
vec.resize(0);
vec.reserve(m.size());
std::for_each(m.begin(), m.end(),
// changed the key type.
[&vec](const std::pair<const int, A> & p) { vec.push_back(p.second); });
std::cout << "START\n";
vec.resize(0);
vec.reserve(m.size());
for (const auto & p : m) {
vec.emplace_back(p.second);
}

Related

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 do i assign values to my fraction objecct using make_unique()?

#include <memory> // for std::unique_ptr and std::make_unique
#include <iostream>
class Fraction
{
private:
int m_numerator;
int m_denominator;
public:
Fraction(int numerator, int denominator) :
m_numerator{ numerator }, m_denominator{ denominator }
{
}
friend std::ostream& operator<<(std::ostream& out, const Fraction &f1)
{
out << f1.m_numerator << "/" << f1.m_denominator;
return out;
}
friend operator=(const Fraction &f1,const int numerator,const int denominator){
f1.m_numerator=numerator;
f1.m_denominator=denominator;
}
};
int main()
{
// Create a single dynamically allocated Fraction with numerator 3 and denominator 5
std::unique_ptr<Fraction> f1{ std::make_unique<Fraction>(3, 5) };
std::cout << *f1 << '\n';
// Create a dynamically allocated array of Fractions of length 4
// We can also use automatic type deduction to good effect here
auto f2{ std::make_unique<Fraction[]>(4) };
f2[0]=(3,5);
f2[1]=(67,82,5,543345);
std::cout << f2[0] << '\n';
std::cout << f2[1] << '\n';
return 0;
}
First, operator= can be implemented only as member function, not free function. So your approach is just wrong. Second, overloaded operator= can accept only one parameter. The closest thing you want, can be achived by passing initializer_list as this parameter:
Fraction& operator=(std::initializer_list<int> il){
// some code validating size of il here
this->m_numerator=*il.begin();
this->m_denominator = *(il.begin()+1);
return *this;
}
the use looks like:
f2[0]={3,5};
f2[1]={67,84};
Full demo

why map emplace calling rvalue constructor twice

class twoMem {
int _a;
int _b;
public:
twoMem() {
std::cout << "default constructor" << std::endl;
}
twoMem(int a, int b) :_a(a), _b(b) {
std::cout << "constructor called" << std::endl;
}
twoMem(const twoMem& other) {
std::cout << "copy constructor called" << std::endl;
_a = other._a;
_b = other._b;
}
twoMem(const twoMem&& other) {
std::cout << "rvalue copy constructor called" << std::endl;
_a = other._a;
_b = other._b;
}
~twoMem() {
std::cout << "destructor called" << std::endl;
}
};
int main()
{
std::map<std::string, twoMem> myMap{};
myMap.emplace(std::make_pair("foo", twoMem{ 1, 2 }));
return 0;
}
output:
constructor called
rvalue copy constructor called
rvalue copy constructor called
destructor called
destructor called
destructor called
First, make_pair moves from twoMem argument into pair<const char*, twoMem> it returns. Second, emplace() moves from that into actual node.
Make it
myMap.emplace("foo", twoMem{ 1, 2 });
then the move constructor is only called once. That's kind of the point of map::emplace.
You can get to zero copy or move constructors this way:
myMap.emplace(std::piecewise_construct, std::make_tuple("foo"), std::make_tuple(1, 2));
Though arguably this cure may be worse than the disease, as now you are constructing and copying tuples around, which is likely at least as expensive as copying twoMem.

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.

Vector move semantic c++11

I have been searching for a while but I couldn't find clear explanation for my doubts.
Mainly:
struct foo
{
foo(int n=0) : m_n(n) {}
int m_n;
};
std::vector<foo> vec;
vec.push_back(100);
vec.push_back(55);
vec.push_back(6);
std::cout << vec.data() << std::endl; //0x1aca010
std::cout << vec.capacity() << std::endl; //4
vec.push_back(6);
vec.push_back(6);
std::cout << vec.data() << std::endl; //0x1aca050 *
std::cout << vec.capacity(); //8
*As I understand a vector is an dynamic array which contains data in one continuous block of memory. When we resize it and there is out of space for more elements, new block of memory is being allocated and all elements are copied to that one. Does it work the same with respect to the move semantic in c++11? It's not a list so pointers can't be just "swaped".
Why do you think pointers can't just be "swapped"? Let me show you a possible implementation >o<
template <typename T>
class vector
{
private:
T *mem;
// ...
public:
// ...
vector(vector &&rhs)
: vector() // delegate constructor
{
rhs.swap(*this);
}
vector &operator =(vector &&rhs)
{
rhs.swap(*this);
rhs.clear();
rhs.shrink_to_fit();
return *this;
}
void swap(vector &obj)
{
using std::swap;
swap(mem, obj.mem);
// ...
}
// ...
};
However, if you want to use a different allocator from an original vector (i.e. to use the sixth constructor of this page), you should allocate new memory and copy data into it, not just swap pointers.

Resources