Home > Software design >  What is the new feature about constexpr std::string with gcc 12.1
What is the new feature about constexpr std::string with gcc 12.1

Time:09-27

case1:

#include <string>

inline constexpr std::string test(std::string s) noexcept
{
    return s   "xxx";
}

int main()
{
    auto s = test("abc");
}

c 20 with gcc 12.1 is built okay, c 17 with gcc 12.1 or c 20/17 with gcc 11.1 was failed,

constexpr std::string, Is this a new feature, or what does this mean?

case2:

#include <string>

int main()
{
    constexpr std::string test{"xxxxxx"};
}

And in this case both failed, what is the difference between these two cases.

CodePudding user response:

The following might not compile either, for you:

constexpr auto s = test("abc");

There's a slight semantical difference between a constexpr object and a constexpr function.

A constexpr object must be a compile-time constant.

A constexpr function might be a compile-time constant if everything in the function is a constant expression. Subsequently, the result of the function is a constant expression if its parameters are, and obviously not if they're not.

What appears to be happening is that your compiler hasn't yet implemented the constexpr version of the std::string constructor in C 20, but has implemented the constexpr operator.

Hence the function gets compiled without a constant expression as its parameter, but since its result is not assigned to a constexpr object this is not immediately apparent.

But assigning the function's result to a constexpr reveals the ugly truth.

CodePudding user response:

There are two different use cases of constexpr here:

  1. constexpr functions
  2. constexpr objects

When you see it on a function such as

inline constexpr std::string test(std::string s) noexcept
{
    return s   "xxx";
}

the constexpr is not part of the return type, in the same way that inline is not part of the return type; it is part of the function definition. In this case, constexpr says "this function can possibly be run at compile time".

The second use case you've mentioned is tagging an object definition as constexpr. In this use case, you're telling the compiler that this must be a compile time constant, and currently std::string objects cannot be marked constexpr due to the dynamic memory allocation that it does which it performed at runtime.

One thing you may have seen is that the std::string constructor was marked constexpr in C 20. This does not mean that you can create constexpr instances of std::string. It just means that std::string can be constructed and used within constexpr contexts (like a constexpr function).

You may then ask "if std::string requires runtime allocation, how can it be used within a constexpr function?". The basic answer is that the compiler uses a different allocation strategy to enable it, and does extra magic to make sure no undefined behaviour occurs. You can see more info here.

To give a bit more information about constexpr functions, note that I said it could "possibly" be run at compile time. A function marked constexpr can be run at either compile time or runtime, depending on the arguments it's called with

constexpr int double_val(int x) { return 2 * x; }

int main() {
    const int y = double_val(4); // likely ran at compile time
    
    int input = 0;
    std::cin >> input;
    const int z = double_val(input); // run at runtime
}

If you want to force the function to only run at compile time, that's what C 20's consteval keyword allows you to do.

consteval int double_val(int x) { return 2 * x; }

int main() {
    const int y = double_val(4); // fine, ran at compile time
    
    int input = 0;
    std::cin >> input;
    const int z = double_val(input); // ERROR, could not compile
}
  • Related