What Is the Reason for Not Being Able to Deduce Array Size from Initializer-String in Member Variable

What is the reason for not being able to deduce array size from initializer-string in member variable?

The reason is that you always have the possibility to override an in-class initializer list in the constructor. So I guess that in the end, it could be very confusing.

struct Foo
{
Foo() {} // str = "test\0";

// Implementing this is easier if I can clearly see how big `str` is,
Foo() : str({'a','b', 'c', 'd'}) {} // str = "abcd0"
const char str[] = "test";
};

Notice that replacing const char with static constexpr char works perfectly, and probably it is what you want anyway.

What is the reason for not being able to deduce array size from initializer-string in member variable?

The reason is that you always have the possibility to override an in-class initializer list in the constructor. So I guess that in the end, it could be very confusing.

struct Foo
{
Foo() {} // str = "test\0";

// Implementing this is easier if I can clearly see how big `str` is,
Foo() : str({'a','b', 'c', 'd'}) {} // str = "abcd0"
const char str[] = "test";
};

Notice that replacing const char with static constexpr char works perfectly, and probably it is what you want anyway.

In class default initializer for array member C++11

A default member initializer is just that - the default. It always allows for the possibility that a constructor will provide a different value. num2letter could potentially be initialized from one of Example's constructors, at which point the default initializer wouldn't even be used... so the type has to be explicitly provided. The declaration has to be valid with our without the initializer.

This is the same reason that you can't do something like:

class Example {
auto i = 4;
};

Deduce std::array size?

C++17 std::array class template argument deduction (CTAD)

Starting with C++17, this new language feature is now used by the standard library and now allows us to omit the template types as well so that the following works:

main.cpp

#include <array>

int main() {
std::array a{1, 2, 3};
}

instead of std::array<int, 3> a{1, 2, 3};

Tested with:

g++ -ggdb3 -O0 -std=c++17 -Wall -Wextra -pedantic -o main.out main.cpp

If we set -std=c++14 instead for example, it fails to compile with:

error: missing template arguments before ‘a’

Tested on Ubuntu 18.04, GCC 7.5.0.

Why I cannot initialize char array in struct

The problem is that array extent cannot be deduced automatically from an in-class initializer. The error message produced by clang is quite clear (see here):

prog.cc:4:34: error: array bound cannot be deduced from an in-class initializer
const char value_in_struct[] = "a";
^

So why is this? In-class initializer can be overridden by a member initializer list of a constructor. So, what if a constructor chooses to initialize the array with something else? In your case, the initializer can be overridden with a brace-enclosed initializer list as follows (aggregate initialization, actually) (see here):

A a = {"abc"};

Since the ultimate initializer cannot be determined at compile time, the compiler cannot deduce the array extent.

Giving the array an explicit extent makes the code compile (see here).

#include <iostream>

struct A {
const char value_in_struct[2] = "a";
};

void t(void) {
char value[] = "a";
std::cout << "value = " << value << std::endl;
}

How to initialize all members of an array to the same value?

Unless that value is 0 (in which case you can omit some part of the initializer
and the corresponding elements will be initialized to 0), there's no easy way.

Don't overlook the obvious solution, though:

int myArray[10] = { 5, 5, 5, 5, 5, 5, 5, 5, 5, 5 };

Elements with missing values will be initialized to 0:

int myArray[10] = { 1, 2 }; // initialize to 1,2,0,0,0...

So this will initialize all elements to 0:

int myArray[10] = { 0 }; // all elements 0

In C++, an empty initialization list will also initialize every element to 0.
This is not allowed with C until C23:

int myArray[10] = {}; // all elements 0 in C++ and C23

Remember that objects with static storage duration will initialize to 0 if no
initializer is specified:

static int myArray[10]; // all elements 0

And that "0" doesn't necessarily mean "all-bits-zero", so using the above is
better and more portable than memset(). (Floating point values will be
initialized to +0, pointers to null value, etc.)

C++ Sized template deduction

with C++20 you can achieve this by adding 2 user defined deduction guides:

template<typename... Ts>
JsonPointer(Ts... ts)->JsonPointer<sizeof...(Ts)>;

template<size_t S>
JsonPointer(JsonPointer<S> existing, const char* suffix)->JsonPointer<S + 1>;

they allow the compiler to find the correct deduction without naming the full template.



template<size_t L>
class JsonPointer {
public:
constexpr JsonPointer(std::array<const char*, L> segments)
: m_path_segments(std::move(segments))
{
}

constexpr JsonPointer(std::initializer_list<const char*> list) : m_path_segments() {
std::copy(list.begin(), list.end(), this->m_path_segments.begin());
}

template<size_t S>
constexpr JsonPointer(JsonPointer<S> existing, const char* suffix) : m_path_segments() {
std::copy(existing.m_path_segments.begin(), existing.m_path_segments.end(),
this->m_path_segments.begin());
this->m_path_segments.back() = std::move(suffix);
}

template<size_t S>
friend class JsonPointer;
private:
std::array<const char*, L> m_path_segments;
};

template<typename... Ts>
JsonPointer(Ts... ts)->JsonPointer<sizeof...(Ts)>;

template<size_t S>
JsonPointer(JsonPointer<S> existing, const char* suffix)->JsonPointer<S + 1>;

int main() {
constexpr JsonPointer base_ptr{ "a", "n" };
constexpr auto ptr = JsonPointer(base_ptr, "test");
}

the constexpr ptr now holds {"a", "n", "test"}

try it out



Related Topics



Leave a reply



Submit