#include <iostream>
#include <vector>
#include <string>
#include <any>
using namespace std;
template <typename T>
class MyVector {
private:
int n;
T* data;
public:
MyVector() {
n = 0;
data = nullptr;
cout << "MyVector default constructor\n";
}
MyVector(int _n) {
n = _n;
data = new T[n];
cout << "MyVector param constructor\n";
}
MyVector(const MyVector& other) {
n = other.n;
data = new T[n];
for (int i=0; i<n; i ) data[i] = other.data[i];
cout << "MyVector copy constructor\n";
}
MyVector(MyVector&& other) {
n = other.n;
data = other.data;
other.n = 0;
other.data = nullptr;
cout << "MyVector move constructor\n";
}
MyVector& operator = (const MyVector& other) {
if (this != &other) {
n = other.n;
delete[] data;
data = new T[n];
for (int i=0; i<n; i ) data[i] = other.data[i];
}
cout << "MyVector copy assigment\n";
return *this;
}
MyVector& operator = (MyVector&& other) {
if (this != &other) {
n = other.n;
delete[] data;
data = other.data;
other.n = 0;
other.data = nullptr;
}
cout << "MyVector move assigment\n";
return *this;
}
~MyVector() {
delete[] data;
cout << "MyVector destructor: size = " << n << "\n";
}
int size() {
return n;
}
};
template <typename T>
any func_any(const any &vec) {
cout << "\nBefore func_any assignment\n";
MyVector<T> res = any_cast<MyVector<T>>(vec); // I want res to const reference MyVector<T> vec, not copy
cout << "\nAfter func_any assignment\n";
return res;
}
template <typename T>
MyVector<T> func(const MyVector<T> &vec) {
cout << "\nBefore func assignment\n";
MyVector<T> res = vec;
cout << "\nAfter func assignment\n";
return res;
}
int main()
{
MyVector<int> a(5);
MyVector<int> a2(6);
cout << "-----------";
cout << "\nBefore func_any call\n";
auto b = func_any<int>(a);
cout << "\nAfter func_any call\n";
cout << "--------------";
cout << "\nBefore func call\n";
auto c = func<int>(a2);
cout << "\nAfter func call\n";
cout << "-----------------";
cout << "\nBefore exit\n";
return 0;
}
I am trying to make a function-executor base class with Python like interface (use std::any
as input and output). In each child class, the actual type input type is known at compile time, so I wish to cast std::any
to a specific type. In the example above I just use a function to make it simple.
However, the function with std::any
call constructors and destructors many times more than the function without. The above program give the following input:
MyVector param constructor
MyVector param constructor
-----------
Before func_any call
MyVector copy constructor
Before func_any assignment
MyVector copy constructor
After func_any assignment
MyVector move constructor
MyVector destructor: size = 0
MyVector destructor: size = 5
After func_any call
--------------
Before func call
Before func assignment
MyVector copy constructor
After func assignment
After func call
-----------------
Before exit
MyVector destructor: size = 6
MyVector destructor: size = 5
MyVector destructor: size = 6
MyVector destructor: size = 5
So the function version that uses std::any
needs 3 constructor 2 destructor calls. While the normal version needs just 1 constructor call. I assume the return
statement in func
is copy elision.
How can I improve this? The most important thing I need is below, if it's impossible with std::any
then please provide a possible solution with std::variant
.
MyVector<T> res = any_cast<MyVector<T>>(vec); // I want res to const reference MyVector<T> vec, not copy
CodePudding user response:
An object needs to be copied into the std::any
, so trying to call func_any<int>(a)
will copy a
.
You can instead hold a std::reference_wrapper<T>
or a T*
pointer in the std::any
:
template <typename T>
any func_any(const any &vec) {
cout << "\nBefore func_any assignment\n";
MyVector<T> res = any_cast<std::reference_wrapper<const MyVector<T>>>(vec);
cout << "\nAfter func_any assignment\n";
return res;
}
auto b = func_any<int>(std::cref(a));
Since you already know what types are actually being used, you might be better off making your base class use void*
as input and output.
You can also any_cast
to a reference type:
const MyVector<T>& res = std::any_cast<const MyVector<T>&>(vec)
This will be a reference to the value held in vec
(so no constructors involved). This will not reduce the overall number of copies, but it will get rid of one move due to NRVO.