1
#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

Huy Le
  • 1,439
  • 4
  • 19
  • 2
    You first convert a _vector_ `a2` to _any_ `vec`. Then, you convert this _any_ `vec` to _vector_ `res`. Finally, you again convert _vector_ `res` to _any_ `b`. Why so many conversions? What is the reason for converting from _any_ to _vector_ and then back to _any_? It's similar as in this live demo: https://godbolt.org/z/n5eMrrG55. – Daniel Langr Mar 25 '22 at 08:52
  • It was just to demonstrate constructor/destructor with std::any. In my actual usage the function return something else, not `res` – Huy Le Mar 25 '22 at 08:59
  • 2
    `vector res = ...` – you instantiate a new vector. Wouldn't you rather want a reference? `vector& res = std::any_cast(...)`? – Aconcagua Mar 25 '22 at 09:00
  • 1
    You can use `const MyVector& res = any_cast&>(vec);` to avoid copy. – Daniel Langr Mar 25 '22 at 09:01
  • 1
    Side note: About [`using namespace std`](https://stackoverflow.com/questions/1452721/why-is-using-namespace-std-considered-bad-practice)... – Aconcagua Mar 25 '22 at 09:01
  • @Aconcagua I tried that and got compiler error. Turns out I forgot to add const. Thanks! – Huy Le Mar 25 '22 at 09:01
  • Not fully sure what you intend to do – but sounds a bit like *polymorphism* might be a good alternative. Have you considered? – Aconcagua Mar 25 '22 at 09:03
  • @Aconcagua I'm making a generic executor that receives a map>, parse the string for function name, then parse the map inside for parameter. So I need a function that can accept any type of parameter and return std::any. I don't know if it's possible with polymorph – Huy Le Mar 25 '22 at 09:10

1 Answers1

3

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.

Artyer
  • 31,034
  • 3
  • 47
  • 75
  • I forgot the const keyword in my `std::any_cast&>(vec)` . Thanks! – Huy Le Mar 25 '22 at 09:01
  • Oh, any_cast to reference still cost a copy. So I guess I have to use reference_wrapper – Huy Le Mar 25 '22 at 09:04
  • @HuyLe It shouldn't be. The copy should be happening when you pass it as a parameter (`func_any(const any& vec)` binds to a tempory `std::any` that copies) – Artyer Mar 25 '22 at 09:11