5

I am trying to initialize a collection of pointers to class A through an initializer list. However, the initializer list cannot use reference as a template type.

I have the following code.

#include <iostream>
#include <functional>
#include <algorithm>
#include <vector>
#include <initializer_list>
#include <memory>

struct A
{
    virtual void f() const noexcept { std::cout << "A"; }
};

struct B : public A
{
    virtual void f() const noexcept override { std::cout << "B"; }
};

class Test
{
    std::vector<std::shared_ptr<A>> vec;
public:
    Test(const std::initializer_list<A>& list)
    //Test(const std::initializer_list<A&>& list) <------ Solution?
    {
        for (auto& x : list)
            vec.push_back(std::make_shared<A>(x));
    }
    void print()
    {
        std::for_each(vec.begin(), vec.end(), [](auto x) { x->f(); });
    }
};

int main()
{
    Test test = { A(), B() };
    test.print();
}

The code prints:

AA

It should print:

AB

Is there a simple way to do this, without having to create pointers in the calling method?

The related article (How do I implement polymorphism with std::shared_ptr?) did not provide much help with this problem.

Community
  • 1
  • 1
Igor Ševo
  • 5,459
  • 3
  • 35
  • 80
  • 4
    Is just using a variadic constructor acceptable, because if so, [wouldn't something like this work](http://ideone.com/4jdhfZ) ? Or is an explicit initializer list a requirement of yours. – WhozCraig Mar 01 '17 at 19:02

3 Answers3

5

std::initializer_list<T> holds the passed objects by value, so there can be no polymorhism. Also std::make_shared<A> always makes an object of type A, not some type that derives from A. You need another approach.

Since you have an arbitrary number of arguments with arbitrary types, you'll probably need a variadic template constructor.

Edit: My code suggestion was way too complicated. The comment by WhozCraig is much better:

template<class... Args>
Test(Args&&... args) :
    vec { std::make_shared<std::remove_reference_t<Args>>(std::forward<Args>(args))... }
{
}
eerorika
  • 232,697
  • 12
  • 197
  • 326
2

You are inserting to the vector using std::make_shared<A>(). You are copying a bunch of B and A into a bunch of A.

If you want the syntax in your main to work, the simplest way to do that is a template :

struct Foo {
    template<typename... Args>
    Foo(Args... args) :
        vec{std::make_shared<Args>(std::move(args))...}
    {}

private:
    std::vector<std::shared_ptr<A>> vec;
};
Guillaume Racicot
  • 39,621
  • 9
  • 77
  • 141
1

You're making new shared_ptrs on the following line

        vec.push_back(std::make_shared<A>(x));

It doesn't matter what x was, you made a new A with its values.

You probably want to create a shared_ptr<A> and pass the pointer from x.get() to the new empty shared_ptr<A>

TankorSmash
  • 12,186
  • 6
  • 68
  • 106