struct Base
{
Base(int a_) : a(a_)
{
}
int a;
};
class Derived: public Base
{
public:
Derived(int i): Base(i)
{
}
Derived(const Derived && rhs)
: Base(std::move(rhs))
{
}
};
int main()
{
Derived d1(2);
Derived d2 = std::move(d1);
std::cout << d1.a << '\n';
}
Why the thing from which we just moved, that is, the thing that we just pilfered, is still accessible on subsequent lines of code? I mean d1.a.
Related
I have some struct like this:
struct A { ... };
struct B { ... };
And I have a template like this:
template<typename struct_arg>
class X { ... }
Now I wanna create an array of arguments as struct like this:
args [2] { A, B };
for (args) {
X<args[i]> x;
}
Can I possible to create an array like this!?
Yes, you can do. std::variant is made for this usage and you can access such members of a variant with std::visit.
But if you do so, keep in mind, that each element of the array has an additional data member which has the type information and that each element has at minimum the size of the largest type you store. And also std::visit comes with a overhead, as a table for access the data member must be created. Typically done in compile time, but sometime g++ generates it in run time, which will decrease speed a lot!
struct A
{
void Do() { std::cout << "A" << std::endl; }
};
struct B
{
void Do() { std::cout << "B" << std::endl; }
};
int main()
{
std::array<std::variant< A,B >,2> arr{ A{}, B{}, B{}, A{} };
for ( auto& element: arr )
{
std::visit( []( auto& vari ) { vari.Do(); }, element );
}
}
Or if you like your encapsulation with an additional strucuture/class like given in your example:
struct A
{
void Do() { std::cout << "A" << std::endl; }
};
struct B
{
void Do() { std::cout << "B" << std::endl; }
};
template < typename struct_arg >
struct X: public struct_arg{};
int main()
{
std::array<std::variant< X<A>,X<B> >,2> arr{ X<A>{}, X<B>{} };
for ( auto& element: arr )
{
std::visit( []( auto& vari ) { vari.Do(); }, element );
}
}
I was implementing the ring buffer and have encountered an error. What does it mean to store a reference of outer class(class ring) object(m_ring) in inner class(class iterator) and when I remove the reference(&) the program compiles correctly but crashes. Please explain what is happening.(See the comment in Ring.h) Sorry for bad English.
// Ring.h
#ifndef RING.H
#define RING.H
#include <iostream>
using namespace std;
template<class T>
class ring {
unsigned int m_size;
int m_pos;
T *m_values;
public:
class iterator;
public:
ring(unsigned int size) : m_size(size), m_pos(0)
{
m_values = new T[m_size];
}
~ring()
{
delete[] m_values;
}
void add(const T &val)
{
m_values[m_pos] = val;
m_pos++;
m_pos %= m_size;
}
T& get(int pos)
{
return m_values[pos];
}
iterator begin()
{
return iterator(0, *this);
}
iterator end()
{
return iterator(m_size, *this);
}
};
template<class T>
class ring<T>::iterator {
int m_pos;
ring &m_ring; // Removing & gives garbage output.
public:
iterator(int pos, ring& aRing) : m_pos(pos), m_ring(aRing){}
bool operator!=(const iterator &other) const
{
return other.m_pos != m_pos;
}
iterator &operator++(int)
{
m_pos++;
return *this;
}
iterator &operator++()
{
m_pos++;
return *this;
}
T &operator*()
{
// return m_ring.m_values[m_pos];
return m_ring.get(m_pos);
}
};
#endif // RING
Driver program :
// Ring_Buffer_Class.cpp
#include <iostream>
#include "ring.h"
using namespace std;
int main()
{
ring<string> textring(3);
textring.add("one");
textring.add("two");
textring.add("three");
textring.add("four");
// C++ 98
for(ring<string>::iterator it = textring.begin(); it != textring.end(); it++)
{
cout << *it << endl;
}
cout << endl;
// C++11
for(string value : textring)
{
cout << value << endl;
}
return 0;
}
I also observed that removing ~ring() (Destructor) results into correct output.
Expected output :
four
two
three
four
two
three
After many trials I still do not understand how to properly take advantage of the move semantics in order to not copy the result of the operation and just use the pointer, or std::move, to "exchange" the data pointed to. This will be very usefull to speed-up more complicated functions like f(g(),h(i(l,m),n(),p(q()))
The objective is to have:
t3={2,4,6};
t1={}; // empty
While executing the code below the output is:
t3={2,4,6};
t1={1,2,3};
Code:
namespace MTensor {
typedef std::vector<double> Tensor1DType;
class Tensor1D {
private:
//std::shared_ptr<Tensor1DType> data = std::make_shared<Tensor1DType>();
Tensor1DType * data = new Tensor1DType;
public:
Tensor1D() {
};
Tensor1D(const Tensor1D& other) {
for(int i=0;i<other.data->size();i++) {
data->push_back(other.data->at(i));
}
}
Tensor1D(Tensor1D&& other) : data(std::move(other.data)) {
other.data = nullptr;
}
~Tensor1D() {
delete data;
};
int size() {
return data->size();
};
void insert(double value) {
data->push_back(value);
}
void insert(const std::initializer_list<double>& valuesList) {
for(auto value : valuesList) {
data->push_back(value);
}
}
double operator() (int i) {
if(i>data->size()) {
std::cout << "index must be within vector dimension" << std::endl;
exit(1);
}
return data->at(i);
}
Tensor1D& operator=(Tensor1D&& other) {
if (this == &other){
return *this;
}
data = other.data;
other.data = nullptr;
return *this;
}
void printTensor(Tensor1DType info) {
for(int i=0;i<info.size();i++) {
std::cout << info.at(i) << "," << std::endl;
}
}
void printTensor() {
for(int i=0;i<data->size();i++) {
std::cout << data->at(i) << "," << std::endl;
}
}
};
} // end of namespace MTensor
In file main.cpp:
MTensor::Tensor1D scalarProduct1D(MTensor::Tensor1D t1, double scalar) {
MTensor::Tensor1D tensor;
for(int i=0;i<t1.size();++i) {
tensor.insert(t1(i) * scalar);
}
//return std::move(tensor);
return tensor;
}
int main() {
MTensor::Tensor1D t1;
t1.insert({1,2,3});
std::cout << "t1:" << std::endl;
t1.printTensor();
MTensor::Tensor1D t3(scalarProduct1D(t1,2));
std::cout << "t3:" << std::endl;
t3.printTensor();
std::cout << "t1:" << std::endl;
t1.printTensor();
return 0;
}
Your use of new is a red flag, especially on a std::vector.
std::vectors support move semantics natively. They are a memory management class. Manual memory management of a memory management class is a BIG red flag.
Follow the rule of 0. =default your move constructor, move assignment, copy constructor, destructor and copy assignment. Remove the * from the vector. Don't allocate it. Replace data-> with data.
The second thing you should do is change:
MTensor::Tensor1D scalarProduct1D(MTensor::Tensor1D t1, double scalar) {
As it stands you take the first argument by value. That is great.
But once you take it by value, you should reuse it! Return t1 instead of creating a new temporary and returning it.
For that to be efficient, you will want to have a way to modify a tensor in-place.
void set(int i, double v) {
if(i>data->size()) {
std::cout << "index must be within vector dimension" << std::endl;
exit(1);
}
data.at(i) = v;
}
which gives us:
MTensor::Tensor1D scalarProduct1D(MTensor::Tensor1D t1, double scalar) {
for(int i=0;i<t1.size();++i) {
ts.set(i, t1(i) * scalar);
}
return t1; // implicitly moved
}
We are now getting close.
The final thing you have to do is this:
MTensor::Tensor1D t3(scalarProduct1D(std::move(t1),2));
to move the t1 into the scalarProduct1D.
A final problem with your code is that you use at and you check bounds. at's purpose is to check bounds. If you use at, don't check bounds (do so with a try/catch). If you check bounds, use [].
End result:
typedef std::vector<double> Tensor1DType;
class Tensor1D {
private:
//std::shared_ptr<Tensor1DType> data = std::make_shared<Tensor1DType>();
Tensor1DType data;
public:
Tensor1D() {};
Tensor1D(const Tensor1D& other)=default;
Tensor1D(Tensor1D&& other)=default;
~Tensor1D()=default;
Tensor1D& operator=(Tensor1D&& other)=default;
Tensor1D& operator=(Tensor1D const& other)=default;
Tensor1D(const std::initializer_list<double>& valuesList) {
insert(valuesList);
}
int size() const {
return data.size();
};
void insert(double value) {
data.push_back(value);
}
void insert(const std::initializer_list<double>& valuesList) {
data.insert( data.end(), valuesList.begin(), valuesList.end() );
}
double operator() (int i) const {
if(i>data.size()) {
std::cout << "index must be within vector dimension" << std::endl;
exit(1);
}
return data[i];
}
void set(int i, double v) {
if(i>data->size()) {
std::cout << "index must be within vector dimension" << std::endl;
exit(1);
}
data.at(i) = v;
}
static void printTensor(Tensor1DType const& info) {
for(double e : info) {
std::cout << e << "," << std::endl;
}
}
void printTensor() const {
printTensor(data);
}
};
MTensor::Tensor1D scalarProduct1D(MTensor::Tensor1D t1, double scalar) {
for(int i=0;i<t1.size();++i) {
t1.set(i, t1(i) * scalar);
}
return t1;
}
int main() {
MTensor::Tensor1D t1 = {1,2,3};
std::cout << "t1:" << std::endl;
t1.printTensor();
MTensor::Tensor1D t3(scalarProduct1D(std::move(t1),2));
std::cout << "t3:" << std::endl;
t3.printTensor();
std::cout << "t1:" << std::endl;
t1.printTensor();
return 0;
}
with a few other minor fixes (like using range-for, DRY, etc).
You need to move t1 when calling scalarProduct1D, otherwise you'll make a copy:
MTensor::Tensor1D t3(scalarProduct1D(std::move(t1),2));
You need to explicitly use std::move because t1 is an lvalue expression.
Note that you'll have to fix your printing functions to avoid dereferencing nullptr if you want accessing the moved-from object to be a valid operation. I instead suggest to avoid making method invocation on moved-from objects valid as it requires additional checks and doesn't follow the idea of "this object has been moved, now it's in an invalid state".
live wandbox example
I want to override virtual methods from base class with template methods in derived class; Just want to know if there is any smart way or work around to make this possible.
#include <iostream>
using namespace std;
struct A
{
virtual void AF(int i)
{
std::cout << "Function in A" << std::endl;
}
virtual void af(int i)
{
std::cout << "Function in A" << std::endl;
}
};
struct B
{
virtual void BF(int i)
{
std::cout << "Function in B" << std::endl;
}
virtual void bf(int i)
{
std::cout << "Function in B" << std::endl;
}
};
template<bool IS_A>
struct C : public A, public B
{
template<class I>
typename std::enable_if<std::is_same<int, I>::value && IS_A,void>::type AF(I i)
{
std::cout << "Function override from A in C" << std::endl;
}
template<class I>
typename std::enable_if<std::is_same<int, I>::value && !IS_A,void>::type BF(I i)
{
std::cout << "Function override from B in C" << std::endl;
}
template<class I>
void af(I i)
{
std::cout << "Function override from A in C" << std::endl;
}
template<class I>
void bf(I i)
{
std::cout << "Function override from B in C" << std::endl;
}
};
int main() {
int i(0);
{
A * a = new C<true>();
a->AF(i);
a->af(i);
}
{
B * b = new C<false>();
b->BF(i);
b->bf(i);
}
return 0;
}
Output is:
Function in A //Want this to be Function override from A in C
Function in A //Want this to be Function override from A in C
Function in B //Want this to be Function override from B in C
Function in B //Want this to be Function override from B in C
Thanks.
First, to "enable/disable" member functions according to IS_A, you can just define both true and false specializations of C:
template <bool IS_A>
struct C;
template <>
struct C<true> : A, B
{
// functions only defined for IS_A == true
};
template <>
struct C<false> : A, B
{
// functions only defined for IS_A == false
}
And to introduce the overriding by the template's, you can override the ones from A and B where you call the template ones:
void af(int i) override { af<int>(i); }
void bf(int i) override { bf<int>(i); }
You can use the CRTP idiom to do that:
#include <iostream>
#include<type_traits>
struct A
{
virtual void AF(int)
{
std::cout << "Function in A" << std::endl;
}
virtual void af(int)
{
std::cout << "Function in A" << std::endl;
}
};
struct B
{
virtual void BF(int)
{
std::cout << "Function in B" << std::endl;
}
virtual void bf(int)
{
std::cout << "Function in B" << std::endl;
}
};
template<typename D>
struct CRTP: public A, public B
{
template<typename I> void CAF(I i) { A::AF(i); }
template<typename I> void af(I i) { A::af(i); }
template<typename I> void CBF(I i) { B::BF(i); }
template<typename I> void cbf(I i) { B::bf(i); }
void AF(int i) override { static_cast<D*>(this)->CAF(i); }
void af(int i) override { static_cast<D*>(this)->caf(i); }
void BF(int i) override { static_cast<D*>(this)->CBF(i); }
void bf(int i) override { static_cast<D*>(this)->cbf(i); }
};
template<bool IS_A>
struct C : CRTP<C<IS_A>>
{
template<class I>
void CAF(I i)
{
std::cout << "Function override from A in C" << std::endl;
}
template<class I>
void CBF(I i)
{
std::cout << "Function override from B in C" << std::endl;
}
template<class I>
void caf(I i)
{
std::cout << "Function override from A in C" << std::endl;
}
template<class I>
void cbf(I i)
{
std::cout << "Function override from B in C" << std::endl;
}
};
int main()
{
int i(0);
{
A * a = new C<true>();
a->AF(i);
a->af(i);
}
{
B * b = new C<false>();
b->BF(i);
b->bf(i);
}
return 0;
}
I have a priority_queue that contains a vector with some objects.
std::priority_queue<std::shared_ptr<Foo>, std::vector<std::shared_ptr<Foo>>, foo_less> foo_queue;
It has a foo_queue function that will order the priority_queue.
Now, from outside of the priority_queue, I want to change some object value that must influenciate the ordering of the priority_queue.
My question is:
How can I set some kind of "refresh" that will trigger the priority_queue to run the foo_queue() in order to keep it orderes all the time?
Make your own priority queue using the standard heap algorithms and a
vector. When you want to change a key, find and remove that value from
the underlying vector and call make_heap on the vector. Alter the key and then
push it back onto the heap. So the cost is a linear search of the vector
to find the value and a call to make_heap (which I think is also linear).
#include <iostream>
#include <vector>
#include <algorithm>
template <class T, class Container = std::vector<T>,
class Compare = std::less<T> >
class my_priority_queue {
protected:
Container c;
Compare comp;
public:
explicit my_priority_queue(const Container& c_ = Container(),
const Compare& comp_ = Compare())
: c(c_), comp(comp_)
{
std::make_heap(c.begin(), c.end(), comp);
}
bool empty() const { return c.empty(); }
std::size_t size() const { return c.size(); }
const T& top() const { return c.front(); }
void push(const T& x)
{
c.push_back(x);
std::push_heap(c.begin(), c.end(), comp);
}
void pop()
{
std::pop_heap(c.begin(), c.end(), comp);
c.pop_back();
}
void remove(const T& x)
{
auto it = std::find(c.begin(), c.end(), x);
if (it != c.end()) {
c.erase(it);
std::make_heap(c.begin(), c.end(), comp);
}
}
};
class Foo {
int x_;
public:
Foo(int x) : x_(x) {}
bool operator<(const Foo& f) const { return x_ < f.x_; }
bool operator==(const Foo& f) const { return x_ == f.x_; }
int get() const { return x_; }
void set(int x) { x_ = x; }
};
int main() {
my_priority_queue<Foo> q;
for (auto x: {7, 1, 9, 5}) q.push(Foo(x));
while (!q.empty()) {
std::cout << q.top().get() << '\n';
q.pop();
}
std::cout << '\n';
for (auto x: {7, 1, 9, 5}) q.push(Foo(x));
Foo x = Foo(5);
q.remove(x);
x.set(8);
q.push(x);
while (!q.empty()) {
std::cout << q.top().get() << '\n';
q.pop();
}
}