C++ "const std::any &" 比没有 std::any 更多的复制构造函数调用

C++ "const std::any &" more copy constructor call than without std::any

#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;
}

我正在尝试使用 Python 类接口(使用 std::any 作为输入和输出)制作函数执行器基础 class。在每个子 class 中,实际类型输入类型在编译时是已知的,所以我希望将 std::any 转换为特定类型。在上面的示例中,我只是使用一个函数来简化它。

但是,带std::any的函数调用构造函数和析构函数的次数比不带的函数多很多次。上面的程序给出了以下输入:

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

所以使用std::any的函数版本需要3次构造函数+2次析构函数调用。而普通版本只需要 1 次构造函数调用。我假设 func 中的 return 语句是复制省略。

我该如何改进? 下面是我需要的最重要的东西,如果用 std::any 不可能,请用 std::variant 提供一个可能的解决方案。

MyVector<T> res = any_cast<MyVector<T>>(vec); // I want res to const reference MyVector<T> vec, not copy

需要将对象复制到 std::any,因此尝试调用 func_any<int>(a) 将复制 a

您可以改为在 std::any:

中持有 std::reference_wrapper<T>T* 指针
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));

既然您已经知道实际使用的类型,您最好让您的基础 class 使用 void* 作为输入和输出。


您还可以any_cast引用类型:

const MyVector<T>& res = std::any_cast<const MyVector<T>&>(vec)

这将是对 vec 中保存的值的引用(因此不涉及构造函数)。这样不会减少整体的份数,但是会因为NRVO去掉一招。