vexrav
vexrav

Reputation: 97

Const Int declared with stoi not considered a const

I wrote a simple test program using CGAL, and running into the following issue:

Point is defined above as

typedef K::Point_d Point;

but I don't think this is relevant.

When I attempt to compile the program as follows:

const int size = 10;
Point P[size];

g++ does so without issue.

If instead I attempt to compile:

const int size = stoi("10");
Point P[size]

I get the following error

error: variable length array of non-POD element type 'Point' (aka 'Point_d<Cartesian_d<double,
  CGAL::Linear_algebraCd<double, std::__1::allocator<double> > > >')

Why is size considered a variable instead of a const when retrieved from a string?

Upvotes: 4

Views: 714

Answers (6)

Davislor
Davislor

Reputation: 15144

To expand on kabanus’ answer, the language standard says that an array bound in C++11 must be something called an integral constant expression. C++17 relaxes this slightly and allows a few more things, but that’s not important right now.

This page at cppreference lists all the things you are not allowed to do inside a constant expression. It basically quotes the language standard.

The relevant section that allows your first example is 8a. You are not allowed to use a variable, unless it’s a const variable initialized to a constant expression. So using the name of a const variable of integral type that is initialized to a constant expression is legal, as in your first example. Numeric constants are constant expressions, so size is a const variable initialized to a constant expression, which is A-OK.

However, due to point 2, function calls are only constant expressions if they are both declared constexpr and already defined. So, for a standard library function to be usable in an array expression in your code, it would need to be declared constexpr and also defined in the header file.

The standard does not say that stoi() qualifies, although I don’t think anything in it directly forbids some implementation from providing that as an extension.

Upvotes: 0

kabanus
kabanus

Reputation: 25980

C++ defines specific things to be constant expressions that are defined to be known at compile time - see constant_expression.

In general functions that are not defined as constexpr are not, regardless of what you pass them. You could try and force it as such:

#include<string>

constexpr int x = std::stoi("10");
int main() {
    return 0;
}

but you will find this still leads to an error:

error: call to non-constexpr function 'int std::stoi(const string&, std::size_t*, int)'

so, you are out of luck as stoi is not a constant expression. If you really insist you could override this using your own implementation for example that in How do I convert a C string to a int at compile time?.

Upvotes: 2

Alexey
Alexey

Reputation: 1226

In the first code sample

const int size = 10;
Point P[size];

the particular value of size can be used already at compile time, since it is known (you've specified it). Therefore the compiler may replace all its uses with particular value, w/o actually creating a variable.

In the second sample

const int size = stoi("10");
Point P[size]

the compiler cannot know the value, since it is deduced by the stoi function at runtime. Therefore it cannot substitute the size of the array (which must be known beforehand to determine now much memory to allocate), and you get the error.

In C++11 there is constexpr idiom, which allows some functions, declared as constexpr, be evaluated at compile time. But in your case stoi isn't constexpr function, so you cannot achieve what you want using this particular function. You can implement your own constexpr stoi, but I don't see much sence in it, since your code in this case would contain somethins like this: my_constexpr_stoi("10"), i.e. the argument is always manually written and always known beforehand. Why don't write just 10?..

Upvotes: 1

Praveen
Praveen

Reputation: 9355

The first const int size = 10; is a compile time const expression and it can be computed during compilation, but const int size = stoi("10") is not a compile time const expression hence it fails to compile. std::stoi is not a constexpr function to evaluated on compile time.

If you want to have this functionality you may need to create a constexpr function to evaluate on compile time.

constexpr bool is_digit(char c) {
    return c <= '9' && c >= '0';
}

constexpr int stoi_impl(const char* str, int value = 0) {
    return *str ?
            is_digit(*str) ?
                stoi_impl(str + 1, (*str - '0') + value * 10)
                : throw "compile-time-error: not a digit"
            : value;
}

constexpr int to_int(const char* str) {
    return stoi_impl(str);
}


int main() {
    constexpr int size = to_int("10");
    int arr[size];
}

This will compile; [copied from here]

Upvotes: 1

PeterT
PeterT

Reputation: 8284

The compiler doesn't understand the semantics of the stoi function. All it sees is you calling a function that returns an integer (whether that function can be inlined and optimized away is a seperate issue from the semantics).

To the compiler there is very little semantic difference between

const int size = stoi("10");

and

const int size = getchar();

As other answers mentioned constexpr being the exception. I just thought I'd illustrate the issue.

Upvotes: 1

W077Y
W077Y

Reputation: 11

stoi is not evaluated at compile time. That means the compiler does not know how big the array should be. if you want to do something like that you have to use a constexpr function (constexpr function). These can be evaluated at compile time, then it works.

Upvotes: 1

Related Questions