2

How do I use a static array element as an index to a static array of objects that are of different template instantiations?

I'm stuck getting the obvious compiler error: invalid template argument for 'N', expected compile-time constant expression.

I don't know if there's a non-c++11 answer. Hopefully there's something modern that I can use in vs2013... :)

I'm trying to store data statically like so:

static const char* size1Array[1] =
{
    "hello"
};

static const char* size2Array[2] =
{
    "foo",
    "bar"
};

static const size_t ARRAYSIZES[2] =
{
    1,
    2
};

// Empty parent
struct DataParent {};

template <size_t N>
struct DataChild : DataParent
{
    DataChild(const char*(*arrIn)[N])
        : arr(arrIn) {}

    const char*(*arr)[N];
};

// The arrays are of different sizes, hence the DataParent to keep them in a static array
static DataParent DataTable[ 2 ] =
{
    DataChild< 1 >(&size1Array),
    DataChild< 2 >(&size2Array)            
};

int main()
{
    int index = 1;
    // The tricky cast that won't compile (ARRAYSIZES[index] 
    std::cout << ((DataChild< ARRAYSIZES[index] >*)&DataTable[index])->arr[0] << std::endl;
}

I want access to the objects in the static array but I can't do it with a non-compile constant. I'm running VS2013 Update 4.

2
  • Why does the size of the array have to be a template parameter? Why can't DataChild just be a normal class that stores a const char**? Commented May 29, 2015 at 3:36
  • I wanted to have data that was pointing to a static const char* []. However, considering your question, I've ultimately decided to use a vector in DataChild instead of the const char* (*arr) to get different sizes of arrays initialized using initializer-lists. Commented Jun 3, 2015 at 18:08

2 Answers 2

2

You have several issues:

  • Slicing issue as you store DataParent as value

so

static DataParent DataTable[2] =
{
    DataChild<1>(&size1Array), // Slicing
    DataChild<2>(&size2Array)  // Slicing
};

should be something like:

static DataChild<1> child1(&size1Array);
static DataChild<2> child2(&size2Array);

static DataParent* DataTable[2] = {&child1, &child2};

and so your cast is no longer UB.

  • And compile time issue:

    with

    const size_t ARRAYSIZES[2] = { 1, 2 };
    int index = 1;
    

    following is not a compile time constant:

    ARRAYSIZES[index] // Not a compile time value.
    
Sign up to request clarification or add additional context in comments.

Comments

0

For some reason using constexpr instead of const in the following line works for me. I don't know why yet. See a question I asked on the subject.

// static const size_t ARRAYSIZES[2] =   // Does not work
static constexpr size_t ARRAYSIZES[2] =  // Works
{
    1,
    2
};

The following statemenent will create two child objects but will store only the parent object, which is empty. I don't know how that will help you.

static DataParent DataTable[ 2 ] =
{
    DataChild< 1 >(&size1Array),
    DataChild< 2 >(&size2Array)            
};

Perhaps you need pointers.

static DataParent* DataTable[ 2 ] =
{
    new DataChild< 1 >(&size1Array),
    new DataChild< 2 >(&size2Array)            
};

or smart pointers.

static std::unique_ptr<DataParent> DataTable[ 2 ] =
{
    new DataChild< 1 >(&size1Array),
    new DataChild< 2 >(&size2Array)            
};

Update

After reading a bit more about constexpr, I understand why

static constexpr size_t ARRAYSIZES[2] =  { ... };

works but

static const size_t ARRAYSIZES[2] =  { ... };

does not work.

A constexpr needs to be evaluatable at compile time while a const need not.

Given

double read_double()
{
   double v;
   std::cin >> v;
   return v;
}

const double v1 = read_double();

is ok. v1 is initialized at run time. Once initialized, it remains constant.

constexpr double v2 = read_double();

is not OK since read_double() is not a constexpr and cannot be evaluated at compile time. Hence, it cannot be used to initialize v1 at compile time.

4 Comments

Oops. Sorry, I made a typo. I've updated the code to reflect that DataChild inherits off of DataParent. So, yes, the DataParent struct is empty, however, the DataChild objects will have the data and be of parent type.
@starpax, I assumed that. My suggestion for using pointers or smart pointers is still valid.
I may be missing something here, how do I access the array from a DataParent* or unique_ptr<DataParent> without a proper cast to the template instantiation of either DataChild<1>* or DataChild<2>? I don't think the allocation in the free store solves the issue of needing a compile-time specified cast. (I can't use constexpr in vs2013 unfortunately).
Oh, I see that your point about pointers was referring to the slicing issue Jarod42 mentioned above.

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.