"No appropriate default constructor" errors when defining TStaticArrays in my class constructor?

I am currently working on a procedurally generated Tychoon-style terrain system. To that end (so far) I have two classes: “ATerrain” and “TerrainTile” – the former has a 2D array of the latter which handles tile-specific properties of the terrain (eg: vertex elevation and direct manipulation of a tile).

Within “TerrainTile” I am have two static arrays. The first is a TStaticArray4 of vertex elevation values. The second is a multidimensional TStaticArray4< TStaticArray2> of x and y coordinate values (relative to the tile’s origin). It’s worth noting that I have absolutely no need to see or manipulate these arrays in Blueprint.

's my definition in TerrainTile.h:

class MGTYCHOON_API TerrainTile
{
    //...
private:
    bool isRendered;
    TStaticArray4<uint32> vertices;
    TStaticArray4< TStaticArray2<uint32>> vertex_xy_coords;
    //...
}

My problem is when I try to set default values in my class constructor. When I do that, I get the following errors:

error C2512: 'TStaticArray4<uint32>' : no appropriate default constructor available
error C2512: 'TStaticArray4<TStaticArray2<uint32>>' : no appropriate default constructor available

is my constructor in TerrainTile.cpp:

TerrainTile::TerrainTile(TStaticArray4<uint32> set_vertices = {0, 0, 0, 0})
{
    isRendered = true;
    vertices = set_vertices;
    vertex_xy_coords = {
        { 0, 0 },     // v0
        { 1, 0 },     // v1
        { 1, 1 },     // v2
        { 0, 1 }      // v3
    };
}

Please forgive me if this is a stupid question or if it has been asked in another form. I have tried to Google/AnswerHub search for a solution for about two hours to no avail. I am very new to C++ and I have a hunch this is a really silly problem with a really simple solution. Obviously I’m failing to understand how to properly construct an Array.

How can I properly declare these arrays and define their default values in the constructor?

Thanks in advance!

Hey mate that dont look correct to me.
Try this instead.

	static TStaticArray4<uint32> vertices;
	static TStaticArray4<TStaticArray4 <uint32> > vertex_xy_coords;

Thanks for the response…

I have changed vertex_xy_coordinates into a static member, since they are shared by every instance of “TerrainTile” and defined that in the .cpp file. I can’t do that with vertices, however, because it is unique to each tile so it cannot be static.

No matter what I do it seems I can’t define TStaticArray4 vertices in my constructor. Neither of these work:

TerrainTile::TerrainTile()
{
    isRendered = true;
    vertices = { 0, 0, 0, 0 };
}

TerrainTile::TerrainTile(TStaticArray4<uint32> set_vertices)
{
    isRendered = true;
    vertices = set_vertices;
}

The idea is to have a default vertices value of { 0, 0, 0, 0 } which can be overridden when the TerrainTile object is created. As you can see, even if I just try to define a value for vertices I still get “no appropriate default constructor available”.

The closest I have gotten to success is by defining vertices in TerrainTile.h and making vertex_xy_coords static:

TStaticArray4<uint32> vertices = { 0, 0, 0, 0 };
static TStaticArray4< TStaticArray2<uint32>> vertex_xy_coords;

TerrainTile.cpp now reads:

TerrainTile::TerrainTile(TStaticArray4<uint32> set_vertices)
{
    isRendered = true;
    vertices = set_vertices;
}

TStaticArray4< TStaticArray2<uint32>> TerrainTile::vertex_xy_coords = {
    { 0, 0 },    // v0
    { 1, 0 },    // v1
    { 1, 1 },    // v2
    { 0, 1 }     // v3
};

This implementation is free from red squiggles and IntilliSense errors, but it still fails to compile. 's the error:

error C2512: 'TStaticArray2<uint32>' : no appropriate default constructor available [...] StaticArray.h [...] MGTychoon

For some reason the compiler is complaining about StaticArray.h. I had to crawl through the compiler output to see which file and which line in my code is triggering this error.

Apparently my declaration of vertex_xy_coords in TerrainTile.h is causing the problem.

edit: fixed some typos and clarified stuff…

I have been working on this all day. Turns out that defining multidimensional TStaticArray objects is rather tedious.

The first problem really is in StaticArray.h (as the compiler complained). The constructor for TStaticArray calls the default constructor for each item in the array:

// TStaticArray constructor from "StaticArray.h"...
TStaticArray()
{
    // Call the default constructor for each element using the in-place new operator.
    for(uint32 ElementIndex = 0;ElementIndex < NumElements;++ElementIndex)
    {
    new(&(*this)[ElementIndex]) TElement;
    }
}

The problem, looking at the class declaration of TStaticArray2 (a “shortcut” for a TStaticArray with two elements), is that it really doesn’t have a default constructor (which makes sense because it needs a provided type):

class TStaticArray2 : public TStaticArray<TElement,2>
{
    typedef TStaticArray<TElement,2> Super;
public:
    TStaticArray2(
        typename TCallTraits<TElement>::ParamType In0,
        typename TCallTraits<TElement>::ParamType In1
        )
    {
        (*this)[0] = In0;
        (*this)[1] = In1;
    }
    // ...

I troubleshooted this problem by creating a blank default constructor for TStaticArray2. As expected it compiled and (obviously) crashed. This helped me understand the underlying problem.

Subsequently playing around with TStaticArray2 (and its partners TStaticArray3 and TStaticArray4) shows that these “shortcuts” cannot be defined as multidimensional in an easy way:

// This will fail to compile...
TStaticArray2< TStaticArray2<uint32>> md_array;

// Something like this must be used...
TStaticArray2<uint32> sub_array_0;
TStaticArray2<uint32> sub_array_1;
TStaticArray2<TStaticArray2*> md_array;
md_array[0] = &sub_array_0;
md_array[1] = &sub_array_1;

Do note, I haven’t tested the above workaround. It’s meant to be more of an illustration. In any case, its purely academic because base TStaticArray objects CAN be defined easily as multidimensional arrays, so using the “shortcut” in this case becomes a waste of time:

// This is fine...
TStaticArray< TStaticArray<uint32, 2>, 2> md_array;

The issue with the TStaticArray base class is that it cannot be defined using the typical {x, y, z} implementation which is oh-so-convenient for dynamic TArray objects:

// These will all fail...
TStaticArray<uint32, 2> some_array = { 0, 3 };
TStaticArray< TStaticArray<uint32, 2>, 2> md_array_0 = {
    { 2, 3 },
    { 1, 5 }
};
TStaticArray< TStaticArray<uint32, 2>, 2> md_array_1;
md_array_1[0] = { 2, 3 };
md_array_1[1] = { 1, 5 };

From my experiments, the only way to define a multidimensional TStaticArray is by accessing each address individually and setting each value individually:

// Using some kind of loop with reference values...
TArray<uint32> some_vals = { 2, 3, 1, 6 };
TStaticArray< TStaticArray<uint32, 2>, 2> md_array;
for (uint32 x = 0; x < 2; x++)
{
    for (uint32 y = 0; y < 2; y++)
    {
        for (uint32 i = 0; i < 4; i++)
        {
            md_array[x][y] = some_vals[i];
        }
    }
}

// Defining each value manually...
TStaticArray< TStaticArray<uint32, 2>, 2> md_array;
md_array[0][0] = 2;
md_array[0][1] = 3;
md_array[1][0] = 1;
md_array[1][1] = 6;

At the end of the day I need to go rework most of what I have using TStaticArray objects to incorporate these hard truths. I am nowhere near skilled enough to dig into the Unreal Engine code to find out why a TStaticArray can’t take definitions in the form of {,} and/or implement such a feature.

If anybody would care to enlighten me as to why all this is true, I’d love to learn. I prefer to use static arrays wherever possible when I know how long an array is going to be (particularly when it involves some arbitrary set of values), and using the {,} style to define such data is incredibly convenient.

Alas, until then I will have to settle with the hard lessons of reality.

Hi,

The issue with the TStaticArray base
class is that it cannot be defined
using the typical {x, y, z}
implementation which is
oh-so-convenient for dynamic TArray
objects

TArray does not support {,} syntax either. We would like to, but it requires std::initializer_list, which is not currently available in all of our supported compilers. We hope to add support for it in future.

If you want compile-time-sized arrays without dynamic memory allocation, you can always use a fixed allocator with TArray. Then you can populate it like this:

TArray<int32, TFixedAllocator<20>> MyArr;
int32 Vals[] = { 1, 2, 3, 4, 5, 6 };
MyArr.Append(Vals, ARRAY_COUNT(Vals));

… which is pretty much identical in execution terms to what an std::initializer_list-aware implementation would do.

Steve

1 Like

Thanks for this tip Steve!

#:heart:

Yeah! Thanks!