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

发布于 2025-01-16 22:13:21 字数 3919 浏览 4 评论 0 原文

#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 作为输入和输出)。在每个子类中,实际类型输入类型在编译时已知,因此我希望将 std::any 强制转换为特定类型。在上面的例子中,我只是使用一个函数来使其简单。

但是,使用 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; res = any_cast>(vec); // 我想要 res 为 const 引用 MyVector; vec,不复制

#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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

〃温暖了心ぐ 2025-01-23 22:13:21

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

您可以在 std::any 中保存 std::reference_wrapperT* 指针:

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));

因为您已经知道什么实际上正在使用类型,您最好让基类使用 void* 作为输入和输出。


您还可以 any_cast 参考type:

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

这将是对 vec 中保存的值的引用(因此不涉及构造函数)。这不会减少副本总数,但会消除由于 NRVO 而导致的一招。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文