没有临时实例的参数转发

时间:2018-01-25 23:19:54

标签: c++ c++11 c++14

我有一个模板化的类,它包装了一个名为mObjects的std :: vector。

Class有一个 insert 函数,它在MyArray实例中转发实际存储类型的参数(我认为它叫做variadic模板参数)。在这个例子中,我存储了MyMesh类型,但它可以是任何类型。

正如你在main()函数中看到的那样,mObjects向量不会增长,它的元素会被反复覆盖。

考虑一种对象池类型的数据结构。

一切都按预期工作。

template <class T>
class MyArray
{
    public:

        MyArray(const int capacity) :
            mObjects(capacity)
        {
            mNextIndex = 0;
        }

        template <typename... Args>
        void insert(Args&&... args)
        {

            mObjects[mNextIndex] = T{ std::forward<Args>(args)... };    //PROBLEMATIC ASSIGNMENT

            //... not relevant code
        }

    private:

        int             mNextIndex;
        std::vector<T>  mObjects;

};



int main()
{
    MyArray<Mesh> sa(2);

    sa.insert("foo",1111);  //goes to mObjects[0]
    sa.insert("bar",2222);  //goes to mObjects[1], and tada, the vector is full

    sa.remove(1111);    //not implemented above, but not relevant. Remove func basically adjusts mNextIndex, so mObjects[0] will be overwritten upon next insert.

    sa.insert("xxx",3333);  //mObjects[0] gets overwritten from "foo" to "xxx" and 1111 to 3333
}

我的问题是上面有一行被评为//问题分配。

mObjects[mNextIndex] = T{ std::forward<Args>(args)... };

当该命令执行时,会发生三件事:

  1. 调用MyMesh(const string s,int x)构造函数,这意味着整个MyMesh在堆栈上被分配。为什么?我只想将转发的参数传递给现有的mObjects [mNextIndex]元素。

  2. operator =(MyMesh&amp;&amp; other)被调用,并通过临时变量和mObjects [mNextIndex]之间的变量进行赋值变量。

  3. ~cVMesh()被调用意味着临时变量释放并死亡。

  4. 我想摆脱#1和#3。所以不要想要“昂贵”的临时对象创建。我只想将传入的MyMesh参数转发/分配给mObjects [mNextIndex]。与std :: vector.emplace_back()的作用类似,但与mNextIndex指向的任何位置相同。

    如何在不实例化临时变量的情况下,只将参数转发到C ++中的现有变量?

    对于completness,这里是MyMesh类,它存储在MyArray类中。当调用构造函数/析构函数/分配运算符时,没有什么特别的只是打印出一些消息:

    class Mesh
    {
    public:
        Mesh()
        {
            cout << "Mesh()" << std::endl;
            mStr = "";
            mId = 99999999;
        }
        Mesh(const string s, int x)
        {
            cout << "Mesh(const string s, int x)" << std::endl;
            mStr = s;
            mId = x;
        }
        ~Mesh()
        {
            cout << "~Mesh()" << std::endl;
        }
        Mesh& operator=(const Mesh& other)
        {
            cout << "operator=(const Mesh& other)" << std::endl;
            cout << mStr << " becomes " << other.mStr << endl;
            cout << mId << " becomes " << other.mId << endl;
            mStr = other.mStr;
            mId = other.mId;
            return *this;
        }
        Mesh& operator=(Mesh&& other) noexcept
        {
            cout << "operator=(Mesh&& other)" << std::endl;
            cout << mStr << " becomes " << other.mStr << endl;
            cout << mId << " becomes " << other.mId << endl;
            mStr = other.mStr;
            mId = other.mId;
            return *this;
        }
        Mesh(const Mesh& other)
        {
            cout << "Mesh(const Mesh& other)" << std::endl;
            mStr = other.mStr;
            mId= other.mId;
        }
        Mesh(Mesh&& other) noexcept
        {
            cout << "Mesh(Mesh&& other)" << std::endl;
            mStr = other.mStr;
            mId = other.mId;
            other.mStr = "";
            other.mId = 99999999;
        }
    
        string mStr;
        int mId;
    
    };
    

4 个答案:

答案 0 :(得分:3)

我认为你想要的是用新值重建向量中的任意元素

#include<vector>

template<class T, class... Args>
void create_at_nth_place(std::vector<T>& v, int n, Args&&...args){
    auto& elem = v[n];
    elem.~T();
    new(&elem) T(std::forward<Args>(args)...);
}

struct S {
    S();
    S(int, bool);
    template<class... Args>
    S(Args&&...);
    S(S&&) noexcept;
    ~S();
};

void f() {
    std::vector<S> v(3);
    create_at_nth_place(v, 2, 4323, false);
    char a = 'a';
    create_at_nth_place(v, 2, 'a', 123, 1232, 32.f, a);
}

链接:https://godbolt.org/g/3K9akZ

答案 1 :(得分:1)

mObjects[mNextIndex] = T{ std::forward<Args>(args)... };行创建一个临时对象,对已存储在指定位置的向量中的对象执行移动(复制)分配,最后销毁临时对象。

整个MyArray类没有用,因为vector已经具有类似的功能。

vector<Mesh> sa;
sa.reserve(2);
sa.emplace_back("foo",1111); // Mesh constructor called once
sa.emplace_back("bar",2222); // Mesh constructor called once again

答案 2 :(得分:1)

您可以添加:

void assign(const string& s, int x)
{
    cout << "assign(const string s, int x)" << std::endl;
    mStr = s;
    mId = x;
}

并使用它:

mObjects[mNextIndex].assign(std::forward<Args>(args)...);

答案 3 :(得分:1)

如果你的网格类很重,你应该考虑使用指针数组,这样可以完全消除虚假副本。不会MyArray<std::shared_ptr<MyMesh>>按原样工作吗?