0

I can achieve the functionality I need using option 3, however I would like to investigate whether it is possible to create the array on the stack instead.

#include <array>
#include <vector>

struct NotDefaultConstructable
{
    NotDefaultConstructable(int val){};
};

int main()
{
    //std::array<NotDefaultConstructable, 5> aA;    // Fails to compile. [On stack]

    //std::vector<NotDefaultConstructable> aV(5); // Fails to compile. [On heap]

    std::vector<NotDefaultConstructable> aV; // Compiles. [ On heap]
    aV.reserve(5);
}
20
  • 2
    It compiles because aV is an empty vector. aV.reserve(5) does not make it an vector of 5 NotDefaultConstructables Commented Nov 19, 2020 at 11:23
  • 1
    Option 3 is creating an empty "array". It contains no objects. So it isn't clear what you're after exactly. Commented Nov 19, 2020 at 11:23
  • 3
    @Harry NotDefaultConstructable aV[5] won't work either: godbolt.org/z/8MvxTo Commented Nov 19, 2020 at 11:55
  • 1
    your options 1,2 and 3 are all very different, hence you need to explain what functionality you want to achive. From the code alone it is not clear. (option 3 is not creating any NotDefaultConstructable objects btw) Commented Nov 19, 2020 at 12:04
  • 1
    @AidanGallagher I think the lifetime management of your objects will be terrible like that; godbolt.org/z/54q9o1 - that's a way of doing what you want, but you need to explicitly construct and destruct the objects yourself Commented Nov 19, 2020 at 12:13

3 Answers 3

2

std::array is an aggregate class type which may have a trivial, implicitly defaulted constructor. If T is not default-constructible, the implicit default constructor is defined as deleted, as per [class.ctor]/5.3.

As this applies in your case, you cannot construct an object of std::array<NotDefaultConstructable, 5> by default construction. You can, however, construct it by means of aggregate initialization:

#include <array>

struct NotDefaultConstructable {
    NotDefaultConstructable(int){};
};

int main() {
    std::array<NotDefaultConstructable, 3> arr{1, 2, 3};
}

In this sense, all elements of a std::array object should arguably be initialized, even if they represent a non-present object (yet to be "filled", if you will).

You could either find a an appropriate static vector container, such as boost::static_vector, or you could e.g. implement a thin wrapper around std::array which stores an array as above as well its runtime size. Another alternative would be to use a std::array of optionals:

#include <array>
#include <iostream>
#include <optional>

struct NotDefaultConstructable {
    NotDefaultConstructable(int val) : val_(val) {};
    int val_;
};

int main() {
    std::array<std::optional<NotDefaultConstructable>, 3> arr{};
    arr[1] = NotDefaultConstructable{42};
    for(const auto& element : arr) {
        if(element.has_value()) {
            std::cout << "has value: " << element.value().val_;
        }  // has value: 42
    }
}

where no dynamic memory allocation takes place (cppreference):

If an optional contains a value, the value is guaranteed to be allocated as part of the optional object footprint, i.e. no dynamic memory allocation ever takes place.

Sign up to request clarification or add additional context in comments.

Comments

1

Judging by the fact that OP mentioned looking into using placement new, I wanted to make a full example of using a stack allocated array as memory for dynamically constructed objects.

Using this method, you can allocate stack memory without initializing it with instances. The problem, however, is that you have to monitor the object lifetime for each instance yourself. In this example, I construct all of them and can then simply assume that all of them have to be destroyed, however monitoring how many instances are alive and should be destroyed is quite messy.

This is why dfrib's answer is much more suitable in your situation, as it allows you to allocate the memory on the stack using std::array<std::optional<Type>, 5> and assign instances later. Object lifetime will also be managed for you, so is much more advisable.

Example 1: Placement New:

#include <array>
#include <iostream>

struct NotDefaultConstructable {
    int Value;

    NotDefaultConstructable(int val) : Value(val) {
        std::cout << "constructed: " << Value << "\n";
    };

    ~NotDefaultConstructable(){
        std::cout << "destructed: " << Value << "\n";
    }
};

int main() {
    // allocate enough memory on the stack for 5 instances 
    char aV[sizeof(NotDefaultConstructable) * 5];
    
    // get a pointer to the first NotDefaultConstructable in that array 
    auto avArray = static_cast<NotDefaultConstructable*>(static_cast<void*>(&aV[0]));

    // use placement new to construct each instance 
    for (auto i = 0; i < 5; ++i) 
        new (&avArray[i]) NotDefaultConstructable((i + 1) * 2);

    // do stuff with the instances 
    for (auto i = 0; i < 5; ++i) 
        std::cout << "instance: " << avArray[i].Value << "\n";

    // destruct them all manually, this is what makes placement new a little 
    // cumbersome. I would advise to use std::optional instead.
    for (auto i = 0; i < 5; ++i) 
        avArray[i].~NotDefaultConstructable();
}

example 1: https://godbolt.org/z/7jW8Pb


Example 2: std::array with std::optional

Here's an example using std::optional, which has minor overhead (about ~4 bytes per object) to achieve much more convenience:

#include <array>
#include <optional>
#include <iostream>

struct NotDefaultConstructable {
    int Value;

    NotDefaultConstructable(int val) : Value(val) {
        std::cout << "constructed: " << Value << "\n";
    };

    ~NotDefaultConstructable(){
        std::cout << "destructed: " << Value << "\n";
    }
};

int main() {
    // allocate enough memory on the stack for 5 instances 
    std::array<std::optional<NotDefaultConstructable>, 5> avArray;

    // use placement new to construct each instance 
    for (auto i = 0; i < 5; ++i) 
        avArray[i] = NotDefaultConstructable((i + 1) * 2);

    // do stuff with the instances 
    for (auto i = 0; i < 5; ++i) 
        std::cout << "instance: " << avArray[i].value().Value << "\n";
}

example 2: https://godbolt.org/z/Ynx8aE

Comments

0

You can try the following

#include <iostream>


struct NotDefaultConstructable {
    NotDefaultConstructable(int val) {
        
    };
};

int main() {
    NotDefaultConstructable aV[5] { 6, 6, 6, 6, 6 };
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.