Search code examples
c++language-lawyerc++20constexprconsteval

GCC, LLVM and MSVC disagree about passing constexpr std::string's content to the runtime world. Who is right?


I tried to create a std::string at compile time and pass its contents into the runtime world. Along that I stumbled about this problem:

#include <algorithm>
#include <string>

template <std::size_t N>
struct ct_to_rt_string {
    consteval ct_to_rt_string(std::string const str) {
        std::copy_n(str.c_str(), N + 1, buffer);
    }

    char buffer[N + 1];
};

constexpr std::string make_ct_std_string() {
    return "compile time text";
}

#include <iostream>

int main() {
    std::cout << ct_to_rt_string<make_ct_std_string().size()>(make_ct_std_string()).buffer;
}

GCC 12.2 with libstdc++ compiles and gives the expected output:

compile time text

LLVM 16 with libc++ complains about not a constant expression because not deallocated memory:

<source>:20:34: error: non-type template argument is not a constant expression
    std::cout << ct_to_rt_string<make_ct_std_string().size()>(make_ct_std_string()).buffer;
                                 ^~~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/clang-16.0.0/bin/../include/c++/v1/__memory/allocator.h:113:38: note: allocation performed here was not deallocated
            return static_cast<_Tp*>(::operator new(__n * sizeof(_Tp)));
                                     ^
1 error generated.
ASM generation compiler returned: 1
<source>:20:34: error: non-type template argument is not a constant expression
    std::cout << ct_to_rt_string<make_ct_std_string().size()>(make_ct_std_string()).buffer;
                                 ^~~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/clang-16.0.0/bin/../include/c++/v1/__memory/allocator.h:113:38: note: allocation performed here was not deallocated
            return static_cast<_Tp*>(::operator new(__n * sizeof(_Tp)));
                                     ^
1 error generated.

MSVC 19.35 complains about the constructor not being a constant expression:

<source>(20): error C7595: 'ct_to_rt_string<17>::ct_to_rt_string': call to immediate function is not a constant expression

Which of the three compilers is right?


Solution

  • I think gcc is correct. Specifically, clang is quite happy to compile this (and it runs correctly):

    int main() {
        constexpr size_t N = make_ct_std_string().size();
        std::cout << ct_to_rt_string<N>(make_ct_std_string()).buffer;
    }
    

    Demo

    So I guess it comes down to when the lifetime of the object used to evaluate the template argument is deemed to end, as @user17732522 points out.

    It will also, FWIW, compile this:

    int main() {
        std::cout << ct_to_rt_string<17>(make_ct_std_string()).buffer;
    }
    

    MSVC, however, still won't compile this code: https://godbolt.org/z/vdaKcaqY6