简体   繁体   中英

How to get the pointer to an object from a parameter pack

I'm having trouble with getting 2 different objects of the same type to have pointers to different objects using a parameter pack. Here is Controller.h:

using expand_type = int[];

template<typename... Ts> class Controller {
public:
    Controller(int i, string s, Ts... args): id(i), name(s) {
        expand_type{ (add_component(&args), 0)... };

    }
    void add_component(Test2* a) {
        device_.push_back(a);
    }
    void print() {
        cout<<name<<":"<<endl;
        cout<<"================="<<endl;
        for (vector<Test2*>::size_type i = 0; i < device_.size(); ++i) {
            cout<<device_[i]->print()<<endl;
        }
        cout<<"================="<<endl;
    }
private:
    vector<Test2*> device_;
    int id;
    string name;
};

and here is my Test2.h:

class Test2 {
public:
    Test2(): x(0) {}
    Test2(int a): x(a) {}
    int print() const {
        return x;
    }
private:
    int x;
};

My problem is when I make two seperate Controller objects, they share their Test2 objects in their respective vectors. Here is my main:

int main() {
    Test2 b(69);
    Test2 c(666);
    Test2 d(943754);
    Controller<Test2, Test2, Test2, Test2> x(2, string("Peter"), 70, b, c, d);
    Controller<> y(2, string("Pietje"));
    Controller<Test2, Test2, Test2, Test2> z(3, string("Jan"), 909, 808, 1, 2);

    x.print();
    y.print();
    z.print();

    cout<<"hello"<<endl;
    return 0;
}

then the output is:

Peter:
=================
0
808
1
2
=================
Pietje:
=================
=================
Jan:
=================
0
808
1
2
=================
hello

I want the different objects of Controller to have different pointers to different Test2 objects, and I'v3 no clue how.

Also, as a side problem; my first pointer always becomes 0, unless it's the only object in the vector.

Any help is appreciated!

Side-stepping the funky behavior, your main problem here is ownership: Does Controller own the devices, and if not who? Let's say the controller doesn't own the devices. Then stuff like this will cause problems

Controller<...> z(3, string("Jan"), 909, 808, 1, 2);
                                    ^^^ temporary object's lifetime ends immediately
                                        it won't have a valid address to point to

Your experience will be much better once you plan it out. Let's say Controller owns the devices: just use a vector of Test2 and you're set.

Next issue is the variadic template. It looks like the device_ s are all the same type, and the only use for the variadic template is to have a variable number of constructor arguments. If so, just use a variadic template for the constructor, and make the class a plain ol' simple type:

class Controller {
public:
    template<typename... Ts>
    Controller(int i, string s, Ts&&... args): id(i), name(s), device_ { std::forward<Ts>(args)... } { }
    // ...
};

Put it all together, and everything works as expected:

#include <vector>
#include <string>
#include <iostream>
using namespace std;

class Test2 {
public:
    Test2(): x(0) {}
    Test2(int a): x(a) {}
    int print() const {
        return x;
    }
private:
    int x;
};

class Controller {
public:
    template<typename... Ts>
    Controller(int i, string s, Ts&&... args): id(i), name(s), device_ { std::forward<Ts>(args)... } { }

    void print() {
        cout<<name<<":"<<endl;
        cout<<"================="<<endl;
        for (auto& device : device_) {
            cout<<device.print()<<endl;
        }
        cout<<"================="<<endl;
    }
private:
    vector<Test2> device_;
    int id;
    string name;
};

int main() {
    Test2 b(69);
    Test2 c(666);
    Test2 d(943754);
    Controller x(2, string("Peter"), 70, b, c, d);
    Controller y(2, string("Pietje"));
    Controller z(3, string("Jan"), 909, 808, 1, 2);

    x.print();
    y.print();
    z.print();

    cout<<"hello"<<endl;
    return 0;
}
Peter:
=================
70
69
666
943754
=================
Pietje:
=================
=================
Jan:
=================
909
808
1
2
=================
hello

Demo: https://godbolt.org/z/bLjgaf


UPDATE: Per your request, let's say Controller only stores raw pointers and doesn't own the Test2 devices. First, you have to pass references to the contructor so the object's address is accessible:

class Controller {
public:
    template<typename... Ts>
    Controller(int i, string s, Ts&... args): id(i), name(s), device_ { &args... } { }
    // ...
};

Then you have to make sure every object you pass it outlives the controller:

Test2 b(69);
Test2 c(666);
Test2 d(943754);
Controller x(2, string("Peter"), b, c, d); // this is fine
Controller y(2, string("Pietje"));         // this is fine
Controller z(3, string("Jan"), 909, 808, 1, 2); // uh oh!

So far, we've only ever talked about stack-allocated Test2 objects. You can also dynamically allocate an object and get a pointer to it. This pointer only has a single owner, and you can transfer ownership with std::move :

std::unique_ptr<Test2> = std::make_unique<Test2>(42);

You can also create a pointer with shared ownership. You can copy this pointer, and the last surviving pointer will clean up the object:

std::shared_ptr<Test2> = std::make_shared<Test2>(42);

BUT, I can't recommend this if you're trying to learn about memory models. A lot of times people use it as a way of avoiding thinking about memory models. If everything has a clear owner, it makes your life much easier.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM