How to get a generalized Swap-function? - c++

Using the example for std::swap on cppreference I tried the following SWAP-template:
#include <algorithm>
#include <iostream>
namespace Ns
{
class A
{
int id{};
friend void swap(A& lhs, A& rhs)
{
std::cout << "swap(" << lhs << ", " << rhs << ")\n";
std::swap(lhs.id, rhs.id);
}
friend std::ostream& operator<< (std::ostream& os, A const& a)
{
return os << "A::id=" << a.id;
}
public:
A(int i) : id{i} { }
A(A const&) = delete;
A& operator = (A const&) = delete;
};
}
template<typename T> void SWAP(T &l, T &r)
{
try { std::swap(l, r); }
catch(...) { swap(l, r); }
}
int main()
{
std::cout << "\n======================\n";
int a = 5, b = 3;
std::cout << "before: " << a << ' ' << b << '\n';
std::swap(a,b);
std::cout << "after: " << a << ' ' << b << '\n';
std::cout << "\n=========\n";
Ns::A p{6}, q{9};
std::cout << "before: " << p << ' ' << q << '\n';
// std::swap(p, q); // error, type requirements are not satisfied
swap(p, q); // OK, ADL finds the appropriate friend `swap`
std::cout << "after: " << p << ' ' << q << '\n';
std::cout << "\n======================\n";
std::cout << "before: " << a << ' ' << b << '\n';
SWAP(a,b);
std::cout << "after: " << a << ' ' << b << '\n';
std::cout << "\n=========\n";
std::cout << "before: " << p << ' ' << q << '\n';
SWAP(p, q);
std::cout << "after: " << p << ' ' << q << '\n';
}
to handle the 'friend' swap-function in the namespace; to have just a single SWAP function to call that will handle all cases.
The compiler-error: swap was not declared in this scope
Why does calling swap() for the namespace work in main but not in the template?
and is there a way to have a generalized 'SWAP'-function to handle all such cases?
(edit)
Thanks to #rici the following change to the template works:
template<typename T> void SWAP(T &l, T &r)
{
using namespace std;
swap(l, r);
}
I would still appreciate an ELI5 for the first part of my question: what/how/why does this work ...

There are 2 problems.
Please first read the definition of 'std::swap' here.
You will read the requirements for the type.
You are using exceptions in your swap function. Remove that.
From the description, you can see that your type must be
Type requirements
T must meet the requirements of MoveAssignable and MoveConstructible.
T2 must meet the requirements of Swappable.
You defined (deleted) a constructor and assign operator. With that the compiler will not create the standard constructors/assign operators for you. Please read about the rule of 5.
Your class is no longer MoveAssignable and MoveConstructible.
Simply remove the deleted operator and constructor.
Like the below. Then it will compile.
#include <algorithm>
#include <iostream>
namespace Ns
{
class A
{
int id{};
friend void swap(A& lhs, A& rhs)
{
std::cout << "swap(" << lhs << ", " << rhs << ")\n";
std::swap(lhs.id, rhs.id);
}
friend std::ostream& operator<< (std::ostream& os, A const& a)
{
return os << "A::id=" << a.id;
}
public:
A(int i) : id{ i } { }
//A(A const&) = delete;
//A& operator = (A const&) = delete;
};
}
template<typename T>
void SWAP(T& l, T& r)
{
std::swap(l, r);
}
int main()
{
std::cout << "\n======================\n";
int a = 5, b = 3;
std::cout << "before: " << a << ' ' << b << '\n';
std::swap(a, b);
std::cout << "after: " << a << ' ' << b << '\n';
std::cout << "\n=========\n";
Ns::A p{ 6 }, q{ 9 };
std::cout << "before: " << p << ' ' << q << '\n';
// std::swap(p, q); // error, type requirements are not satisfied
swap(p, q); // OK, ADL finds the appropriate friend `swap`
std::cout << "after: " << p << ' ' << q << '\n';
std::cout << "\n======================\n";
std::cout << "before: " << a << ' ' << b << '\n';
SWAP(a, b);
std::cout << "after: " << a << ' ' << b << '\n';
std::cout << "\n=========\n";
std::cout << "before: " << p << ' ' << q << '\n';
SWAP(p, q);
std::cout << "after: " << p << ' ' << q << '\n';
}

Related

How to std::forward class template parameter

I'm getting compiler error when using std::forward on a class template parameter, but not on a function template parameter. I'd like to know how to std::forward a class template parameter. This is my code:
#include <iostream>
#include <vector>
template<typename T>
class Data_List {
std::vector<T> data_list;
public:
Data_List() = default;
~Data_List() = default;
std::size_t get_list_size() {
return data_list.size();
}
void add_to_list(T&& data) {
std::cout << "\nbefore inside add_to_list: " << data.size() << std::endl;
data_list.push_back(std::forward<T>(data));
std::cout << "after inside add_to_list: " << data.size() << std::endl;
}
};
template<typename T>
void print(T&& t) {
}
int main() {
Data_List<std::vector<int>> list_of_data;
std::vector<int> data(100, 2);
std::cout << "\n1. before size: " << data.size() << std::endl;
std::cout << "1. before Data_List size: " << list_of_data.get_list_size() << std::endl;
print(data);
// list_of_data.add_to_list(data); // gives compiler error here
std::cout << "\n1. after size: " << data.size() << std::endl;
std::cout << "1. after Data_List size: " << list_of_data.get_list_size() << std::endl;
std::cout << "--------------------------------------------------------------------------\n" << std::endl;
std::cout << "\n2. before size: " << data.size() << std::endl;
std::cout << "2. before Data_List size: " << list_of_data.get_list_size() << std::endl;
print(std::move(data));
list_of_data.add_to_list(std::move(data));
std::cout << "\n2. after size: " << data.size() << std::endl;
std::cout << "2. after Data_List size: " << list_of_data.get_list_size() << std::endl;
std::cout << "--------------------------------------------------------------------------\n" << std::endl;
}
This is the error I'm getting cannot bind rvalue reference of type ‘std::vector<int>&&’ to lvalue of type ‘std::vector<int>’
In here:
void add_to_list(T&& data) {
std::cout << "\nbefore inside add_to_list: " << data.size() << std::endl;
data_list.push_back(std::forward<T>(data));
std::cout << "after inside add_to_list: " << data.size() << std::endl;
}
data isn't a forwarding reference, it's an rvalue reference, so you can't do something like list_of_data.add_to_list(data); because data is an lvalue.
To create a forwarding reference, the type must exist as a template parameter of the same function template (Give a reading to the forwarding references part of this cppreference.com page and look specifically at the first example):
function parameter of a function template declared as rvalue reference to cv-unqualified type template parameter of that same function template.
Basically, what you want to do is this:
// ...
template <typename U>
void add_to_list(U&& data) {
std::cout << "\nbefore inside add_to_list: " << data.size() << std::endl;
data_list.push_back(std::forward<U>(data));
std::cout << "after inside add_to_list: " << data.size() << std::endl;
}
// ...
Then data becomes a forwarding reference and does what is intended.
Demo

std::move triggered destructor?

I'm adapting to an interface with parameter const vector<pair<int32_t, A>>& as.
To avoid multi-construct and multi-destruct, I write CreateAs as follow.
I was expecting it to trigger create move del each for once, but it turned out triggered move and del for twice.
What's the reason?
I made this way so that the A objects could destroy themselves automatically, even with new but without delete. Am I doing it right?
To reproduce it: https://repl.it/#unix1/ShrillFirsthandAdvance
#include <iostream>
#include <vector>
using namespace std;
struct A {
A() { cout << "A()" << endl; }
A(int32_t a) : a_(a) { cout << "Create A: " << a << endl; }
A(const A& oa) { a_ = oa.a_; cout << "Copy A: " << oa.a_ << endl; }
A(A& oa) { a_ = oa.a_; cout << "Copy A non-const: " << oa.a_ << endl; }
A(const A&& oa) { a_ = oa.a_; cout << "Move A: " << oa.a_ << endl; }
A(A&& oa) { a_ = oa.a_; cout << "Move A non-const: " << oa.a_ << endl; }
~A() { cout << "Del A: " << a_ << ", ptr: " << this << endl; }
int32_t a_;
};
void CreateAs(vector<pair<int32_t, A>>& as) {
as.reserve(3);
for (int32_t i = 0; i < 3; ++i) {
A* a = new A(i*i);
cout << "a ptr: " << &a << endl;
cout << "-----before insert----" << endl;
as.emplace_back(make_pair(i, move(*a)));
cout << "-----after insert-----" << endl;
}
}
void Test() {
vector<pair<int32_t, A>> as;
cout << "-----Create begin----" << endl;
CreateAs(as);
cout << "-----Create end------" << endl;
for (const auto& item : as) {
cout << item.first << "->" << item.second.a_ << endl;
}
}
int main(int32_t argc, char* argv[]) {
Test();
cout << "____end test____" << endl;
return 0;
}
make_pair constructs an A in a pair. emplace_back moves the pair and therefore A into the vector. The moved from pair is destroyed, also destroying the contained A.
To avoid any move, you might do
void CreateAs(std::vector<std::pair<int32_t, A>>& as) {
as.reserve(3);
for (int32_t i = 0; i < 3; ++i) {
as.emplace_back(i, i*i);
}
}
Demo
You currently have extra move with your extra make_pair.

static unordered_map initialization with unions

The following static initializer list M fails but N suceeds, it seems I cannot figure out how to do the unordered_map initialization when a union of 3 const char * pointers is added to the init-list. I tried the . notation to access the fields of the union and the union itself to no avail.
#include <iostream>
#include <unordered_map>
#include <iterator>
struct C {
int j;
int i;
};
struct D {
int j;
char c;
};
struct E {
const char *a;
const char *b;
const char *c;
}e;
struct A {
const char *s;
union {
C c;
D d;
E e;
} u;
};
std::unordered_map<unsigned int, A> m ({
{1, {"tom",53,'o'}},
{2, {"ming",40,41}},
{3, {"peter","a","b","c"}} });
std::unordered_map<unsigned int, A> n ({
{1, {"tom",53,'o'}},
{2, {"ming",40,41}} });,
int main(void) {
for ( const auto& i : m) {
const auto& j = i.second;
if (i.first == 1)
std::cout << "key: " << i.first << " value: " << j.u.d.j << ":" << j.s << ":" << j.u.d.c << std::endl;
else if (i.first == 2)
std::cout << "key: " << i.first << " value: " << j.u.c.j << ":" << j.s << ":" << j.u.c.i << std::endl;
else if (i.first == 3)
std::cout << "key: " << i.first << " value: " << j.u.e.a << ":" << j.s << ":" << j.u.e.c << std::endl;
}
for ( const auto& i : n) {
const auto& j = i.second;
if (i.first == 1)
std::cout << "key: " << i.first << " value: " << j.u.d.j << ":" << j.s << ":" << j.u.d.c << std::endl;
else if (i.first == 2)
std::cout << "key: " << i.first << " value: " << j.u.c.j << ":" << j.s << ":" << j.u.c.i << std::endl;
}
return 0;
}
Error:
map.cpp:41:32: error: no matching function for call to ‘std::unordered_map::unordered_map()’

Print smart pointer vector using a template function

I wrote a function to print a given vector.
template <typename T>
void print_vector(std::string text, std::vector<T> &vect){
std::cout << ">>>>>>>>>> " << text << " <<<<<<<<<<" << std::endl;
for(T &t: vect){
std::cout << t << ", ";
}
std::cout << std::endl << "--------------------" << std::endl;
}
But when I give a vector of shared_ptr to the function, it prints the address, but not the pointed value.
Is there a way to print the value when the element is a shared_ptr..?
I tried the following way, but it gives me a compile error and I can't figure out how to fix it.
template <typename T, typename F>
void print_vector(std::string text, std::vector<T> &vect){
std::cout << ">>>>>>>>>> " << text << " <<<<<<<<<<" << std::endl;
for(T &t: vect){
if(std::is_same<T, std::shared_ptr<F>>::value) {
std::cout << *t << ", ";
} else {
std::cout << t << ", ";
}
}
std::cout << std::endl << "--------------------" << std::endl;
}
Overload your function for vectors of smart pointers.
template <typename T>
void print_vector(std::string text, std::vector<T> &vect){
std::cout << ">>>>>>>>>> " << text << " <<<<<<<<<<" << std::endl;
for(T &t: vect){
std::cout << t << ", ";
}
std::cout << std::endl << "--------------------" << std::endl;
}
template <typename T>
void print_vector(std::string text, std::vector<std::shared_ptr<T>> &vect){
std::cout << ">>>>>>>>>> " << text << " <<<<<<<<<<" << std::endl;
for(auto &t: vect){
std::cout << *t << ", ";
}
std::cout << std::endl << "--------------------" << std::endl;
}
If you have more cases where you wish to print something different, you may find that some of the overloads are ambiguous, you may have to disable the ambiguous templates when they should match each other.
You can overload operator for shared_ptr check out the following code:
template<typename T>
ostream& operator<<(ostream& out, const shared_ptr<T>& s_ptr)
{
if (s_ptr != nullptr)
out << (*s_ptr);
return out;
}
template <typename T>
void print_vector(std::string text, std::vector<T> &vect){
std::cout << ">>>>>>>>>> " << text << " <<<<<<<<<<" << std::endl;
for(T &t: vect){
std::cout << t << ", ";
}
std::cout << std::endl << "--------------------" << std::endl;
}
you want to overload the stream operator for shared_ptr, this could either be for a specific type or for all types:
template <typename T>
std::ostream& operator << (std::ostream& os, const std::shared_ptr< T >& p)
{
if ( p )
{
os << *p;
}
else
{
os << "<null>";
}
return os;
}
Just overload the function template:
template <typename T>
void print_vector(std::string text, std::vector<T> &vect){
std::cout << ">>>>>>>>>> " << text << " <<<<<<<<<<" << std::endl;
for(T &t: vect){
std::cout << t << ", ";
}
std::cout << std::endl << "--------------------" << std::endl;
}
template <typename T>
void print_vector(std::string text, std::vector<std::shared_ptr<T>> &vect) {
std::cout << ">>>>>>>>>> " << text << " <<<<<<<<<<" << std::endl;
for(auto &t: vect){
std::cout << *t << ", ";
}
std::cout << std::endl << "--------------------" << std::endl;
}
Now it will handle both cases.

Comparison of Virtual Function Pointers in C++

Say I want to check to see whether a subclass has implemented one of it's parent's virtual functions (never mind whether this smells of bad architecture... it's an exercise). If I wanted to see if two regular functions were identical, I could just check &f == &g.
// Plain old functions
void f() {}
void g() {}
...
std::cout << "&f " << &f << "\n"; // "1" OK, for some reason func ptrs are converted
std::cout << "&g " << &f << "\n"; // "1" to booleans when printed. I can dig it.
std::cout << "&f == &g " << (&f == &g) << "\n"; // "0" Good, &f and &g are unequal as expected.
But with virtual member functions, behavior is different.
// Base class with a virtual
struct A {
virtual void f() {}
};
// Subclass which implements f
struct B : public A {
void f() {}
};
// Subclass which doesn't implement f
struct C : public A {};
...
std::cout << "&A::f " << &A::f << "\n"; // "1"
std::cout << "&B::f " << &B::f << "\n"; // "1"
std::cout << "&C::f " << &C::f << "\n"; // "1" ... okay ... annoying, but alright.
std::cout << "&A::f == &B::f " << (&A::f == &B::f) << "\n"; // "1" DANGER - why does &A::f == &B::f if &f != &g?
std::cout << "&A::f == &C::f " << (&A::f == &C::f) << "\n"; // "1"
std::cout << "&B::f == &C::f " << (&B::f == &C::f) << "\n"; // "1"
std::cout << "(void*)&A::f " << (void*)&A::f << "\n"; // "0x4084b0" Here's what I was actually looking for.
std::cout << "(void*)&B::f " << (void*)&B::f << "\n"; // "0x4084bc" Good - the B::f differs from A::f as it should
std::cout << "(void*)&C::f " << (void*)&C::f << "\n"; // "0x4084b0" Perfect.
std::cout << "(void*)&A::f == (void*)&B::f " << ((void*)&A::f == (void*)&B::f) << "\n"; // "0"
std::cout << "(void*)&A::f == (void*)&C::f " << ((void*)&A::f == (void*)&C::f) << "\n"; // "1"
std::cout << "(void*)&B::f == (void*)&C::f " << ((void*)&B::f == (void*)&C::f) << "\n"; // "0" These are the comparison results I want
So my question is marked by DANGER in the code above. Why does &A::f == &B::f if &f != &g? Is there a way to do the comparison I want without casting to void* (which gives off noisy compiler warnings thanks to -Wpmf-conversions)?