c++translationconstexpr

How Do Compilation and Linking Affect constexpr Declarations Across Multiple Files in C++?


I'm trying to understand the relationship between compilation, linking, and the determination of constexpr values in C++. I know that constexpr values need to be fully resolved at compile-time, but some discussions suggest that these are determined during the entire translation process, which includes both compilation and linking. For instance, consider the following excerpt from "Effective Modern C++" book that has confused me:

"Conceptually, constexpr indicates a value that’s not only constant, but is also known during compilation. Technically, their values are determined during translation, and translation consists not just of compilation but also of linking. Unless you write compilers or linkers for C++, however, this has no effect on you, so you can blithely program as if the values of constexpr objects were determined during compilation."

In my case, I wrote the following code in source.cpp:

#include <string_view>
constexpr std::string_view c = "Hello world";

constexpr  std::string_view  func()
{
    return c;
}

In main.cpp, I attempted to use forward declarations for these constexpr values but encountered issues that prevented successful compilation.

#include <array>
#include <iostream>
#include <string_view>

constexpr  std::string_view  func();

int main()
{
    constexpr  std::string_view s = func();
    constexpr size_t l = s.size();
    std::array<int, l> arr;
    std::cout << arr.size() << '\n';
}

Now I am confused based on what the book says and it seems to be in contrast with this example and the following errors:

main.cpp:10:22: Constexpr variable 'l' must be initialized by a
constant expression main.cpp:10:28: initializer of 's' is not a
constant expression main.cpp:9:33: declared here

but when i place all the code in main.cpp it compiles.


Solution

  • The way constant expressions and constexpr are specified by the standard is done carefully so that only the compilation step by the compiler must be able to perform the constant evaluation.

    The linker does not need to be aware of any of it and constant evaluation can't cross over translation units. I don't know what the book's quote is attempting to say, except maybe that it is a (non-informative) technicality about the standard formally specifying everything in terms of an overall "translation" instead of separate "compilation" and "linking" stages.

    In particular, even if a function is declared constexpr, it can still only be used as part of constant evaluation if it is defined (in the same translation unit) before the constant expression in which it is used. This explains why your example trying to initialize s by a call to func fails in the translation unit that lacks a definition of func.

    Also, even if you didn't declare s and the other variables as constexpr (which forces the initialization to be a constant expression or will fail compilation otherwise), the program would still be IFNDR (ill-formed, no diagnostic required), because constexpr on a function implies inline and an inline function must be defined (somewhere) in every translation unit in which it is (odr-)used.

    And then even further, the program would still be IFNDR, even if you did add a definition of func to the main translation unit, because const (implied by constexpr) variables have internal linkage by default in C++. So the two definitions of func in the two translation units would refer to different entities c, making the two definitions incompatible for ODR purposes.