1

Consider a class whose default constructor takes in the file path as a parameter.

class Test
{
    public:
         Test(const std::string& filepath);
    ...
    ...
};

Now I wish to create and initialize an array of Test objects using unique pointers in VS2017.

int main()
{
    std::unique_ptr<Test[]> m_Tests;
    int testCount = 2;
    std::string path1, path2;

    m_Tests = std::make_unique<Test[]>(testCount);    // This line gives a compilation error
    m_Tests[0] = std::make_unique<Test>(path1);
    m_Tests[1] = std::make_unique<Test>(path2);
}

How can I make this work?

Majis
  • 203
  • 1
  • 2
  • 6
  • Related reading: [make_unique arrays, original proposal vs. final](https://stackoverflow.com/q/16755415/1553090) – paddy Feb 25 '20 at 06:30
  • 1
    This would be much easier if you used `std::vector` instead of `std::unique_ptr`. Is there any reason you are not using `std::vector`? – walnut Feb 25 '20 at 06:32
  • FYI: [SO: How to initialize elements of an array managed by a unique_ptr?](https://stackoverflow.com/a/52217532/7478597) – Scheff's Cat Feb 25 '20 at 06:33

3 Answers3

2

g++ 9.2.0 tells me that you lack default constructor, i.e. one without parameters. Adding such constructor works fine. If it's not what you want, you can create array of unique_ptr's, so std::unique_ptr<std::unique_ptr<Test>[]> and after that initialize each element by hand, something similar to this:

#include <memory>
#include <algorithm>
#include <iostream>

struct Test {
std::string str_;
Test(std::string const& str) : str_(str) { }
void print() { std::cout << str_ << '\n'; }
};

int main()
{
    std::unique_ptr<std::unique_ptr<Test>[]> m_Tests;
    int testCount = 2;
    std::string path1{"a"}, path2{"b"};

    m_Tests = std::make_unique<std::unique_ptr<Test>[]>(testCount);
    std::array<std::string, 2> paths{path1, path2};
    std::transform(paths.begin(), paths.end(), &m_Tests[0],
        [](auto const& p) { return std::make_unique<Test>(p); });

    for (int i = 0 ; i < testCount ; ++i) {
            m_Tests[i]->print();
    }
}

Jędrzej Dudkiewicz
  • 1,053
  • 8
  • 21
0

There is no overload of std::make_unique that does this, so you would need to use new directly:

m_Tests.reset(new Test[testCount]{path1, path2});

This will however only compile if testCount is a constant expression, so you need to change the definition int testCount = 2; to const int or constexpr int.

If testCount is not a constant expression, there needs to be a default constructor defined for the case that testCount is smaller than 2 at runtime.

So, really, you probably want to ignore testCount and just let the array size be deduced:

m_Tests.reset(new Test[]{path1, path2});

It would be much easier if you just used std::vector:

std::vector<Test> m_Tests;

//...

m_Tests.emplace_back(path1);
m_Tests.emplace_back(path2);
walnut
  • 21,629
  • 4
  • 23
  • 59
-1

How about you use std::array and can you get rid of testCount (or use it as constexp) then the code can be like below.

class Test
{
public:
    Test(const std::string& filepath){}
};

int main()
{
    constexpr int testCount = 2;
    std::unique_ptr<std::array<Test, testCount>> m_Tests;
    std::string path1, path2;

    m_Tests = std::make_unique<std::array<Test, testCount>>(std::array<Test, testCount>{path1,path2});
}