Home > OS >  C "const std::any &" more copy constructor call than without std::any
C "const std::any &" more copy constructor call than without std::any

Time:03-25

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

  • Related