2

Im trying to create an array with the string::size member but its complaining at the declaration of "char message[msgSize]" that the expression did not evalute to a constant.

However it alows me to declare "msgSize".

How can this be? Why am i allowed to make a constant but i'm not allowed to use it to something that needs a const.

If the answer is that: "the size of the string could change" then the same argument can be made for using sizeof(), but that works.

const unsigned int msgSize = 
        saveAs.size() +
        sizeof("NOTE|  sent get request to: ") + 10;

    char message[msgSize];
    memset(message, 0, msgSize);
    sprintf_s(message, msgSize,"NOTE| sent get request to: %s", saveAs.c_str());
    _cRec->output(message);
10
  • 8
    sizeof can be evaluated at compile time while string.size() cannot. Commented Apr 19, 2017 at 23:28
  • 8
    There's an important difference between a const variable and a constant expression. Commented Apr 19, 2017 at 23:29
  • const is not constexpr. If your compiler can figure a const is a constant expression, as with the sizeof, it might let you use it as a constexpr variable, but not if you throw in a runtime-only expression such as saveAs.size() Commented Apr 19, 2017 at 23:29
  • 1
    Since you're using C++, a std::string would be much more appropriate here. Commented Apr 19, 2017 at 23:35
  • 1
    My point is that manually managing string buffer sizes is where bugs live - let the C++ string class deal with that crap to the maximum extent possible. For example, why did you add 10 to msgSize? Just to make extra sure that there was enough space for the message? Or does `output() actually write past the end of the passed in string? Commented Apr 20, 2017 at 0:04

5 Answers 5

1

Well const just means that you promise that the value won't change. And you can even override that with a const_cast.

What compiler needs there is a value that can be evaluated at the compile time. This is a stronger requirement. Such values are marked with constexpr keyword.

Unfortunately you are out of luck with std::string... size() is not a constexpr.

I played with the following example:

#include <iostream>
#include <string>
using namespace std;

const string msg("Some msg");
constexpr int msg_len = msg.size();

int main() {

    char msg[msg_len];

    cout << sizeof(msg);
    return 0;
}

The thing is that size() is not a constexpr, and you cannot make string a constexpr because it has a non-trivial destructor.

Why it is designed this way is beyond me. Seems like a counter intuitive and serious limitation. However, there are pretty clever implementation of string, for example with small storage optimization. It might be hard to make them constexpr with out serious changes in the implementation.

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

3 Comments

"Why it is designed this way is beyond me." How could std::string possibly be constexpr when it involves dynamic allocation at runtime?
Have you read following sentences? There are more implementations, so your statement is not universally true. The question is about design. You give technical detail which is a consequence. Maybe c++ is lacking const-string type?
There are not "more implementations"; SSO exists but it is not possible at compile-time to know whether it will be sufficient or whether further resizing will be required. Supporting that requires either dynamic allocation, or infinite statically-allocated space. As for a const-string type, I'd like to see that; perhaps some thin-ish wrapper around a char[n]. It would be very limited, though.
0

You would have to make your array dynamic. The size of the string is not known at compile time, but only at run time.

Also, your listing is not a minimal compilable example. I can only assume saveAs is an std::string.

The following would work:

int main()
   {
        std::string myString("My string");
        char * myCStyleString = new char[myString.size()];

        delete[] myCStyleString;
   }

because we are dynamically allocating an array there.

What are you trying to do? It looks like you just want to copy a filename into a message string. Just keep using std::string;

   int main()
   {
        std::string myString("My string");
        std::string message = "I like beans and rice with: ";
        message += myString;

        return 0;
   }

but again, I'd need a minimal compilable example to determine your goal.

Comments

0

In C++ you cannot declare VLAs.

When you declare an array, for example:

char message[x];

The expression x must to be valuable at compile time, that is its value has to be well-known when the source is compiled.

In you example:

char message[msgSize];

The expression (variable) msgSize is not known at compile time, but only at run time. That because the compiler has to know how many bytes reserve in the stack.

Note: the variable msgSize is a constant value. A constant value is not an expression evaluate at compile time. It simply means that its value cannot change once has been assigned.

In C++11 has been introduced the keywork constexpr in order to define an expression which should be evaluate at compile time.

However in your case there is not way to get the size of a dynamic string at compile time. So what you have to do is use dynamic array (dynamic memory).

char* message = new char[msgSize];
// now you have an array of msgSize lenght
// ... do stuff...
// do not forget to release the memory when end
delete[] message;

Finally, I suggest you to re-elaborate your code because it's likely you don't need a dynamic array of chars, but just a std::string. Indeed you can use overloaded operator + in order to concatenate strings and the method std::string::c_str() to access as const char* for backward compatibility.

Comments

0

Why am i allowed to make a constant but i'm not allowed to use it to something that needs a const.

This is because the "constant" expression you have is made up of non constant parts i.e. the size method of the string class. It is not truly constant in terms of having a known value at compile time.

Consider using constexpr variable/function in the future

If the answer is that: "the size of the string could change" then the same argument can be made for using sizeof(), but that works.

No that same argument cannot be used for sizeof because sizeof does not need it's arguement to be a constant.


If you know for a fact that saveAs contains a string with a known size, then perhaps it would be best if you declare that size as a constant and then refer to it in your calculation:

constexpr unsigned int msgSize = 
        SAVEAS_SIZE +
        sizeof("NOTE|  sent get request to: ") + 10;

Then this will allow you to do:

char message[msgSize];

Comments

0

You need a compile-time constant with char message[msgSize]; because this is a local variable which uses static memory that is allocated in the data segment, so the compiler needs to calculate the number of bytes required by the code, including the local variables and arrays.

You can use dynamic memory to solve your problem. The dynamic memory is allocated in the heap. In C, you should use malloc(). In C++, you should use new[] (or better, std::vector, or even std::string). Then you can specify the memory size using a runtime value in a variable.

So, your code would look more like the follow:

char* message = new char[msgSize]; //Allocated memory
//Do everything that you need...
delete[] message; //Release memory

Or:

#include <vector>

std::vector<char> message(msgSize); //Allocated memory
//Do everything that you need...

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.