3
#include <algorithm>

struct S
{
    static constexpr int X = 10;
};

int main()
{
    return std::min(S::X, 0);
};

If std::min expects a const int&, the compiler very likely would like to have the S::X also defined somewhere, i.e. the storage of S::X must exists.

See here or here.

Is there a way to force the compiler to evaluate my constexpr at compile time?

The reason is:

Initially, we had a problem in early initialization of static variables in the init priority. There was some struct Type<int> { static int max; };, and some global static int x = Type<int>::max;, and some other early code other_init used that x. When we updated GCC, suddenly we had x == 0 in other_init.

We thought that we could avoid the problem by using constexpr, so that it would always evaluate it at compile time.

The only other way would be to use struct Type<int> { static constexpr int max(); }; instead, i.e. letting it be a function.

Community
  • 1
  • 1
Albert
  • 65,406
  • 61
  • 242
  • 386
  • 4
    `constexpr` doesn't fix the Static Initialization Order Fiasco. – chris Jun 20 '14 at 07:48
  • Can you post the code which is giving you problems? I mean the one with struct Type. I'd be interested in seeing it. – Marco A. Jun 20 '14 at 08:54
  • @MarcoA.: What code do you mean? It's already there. Let `other_init` be `__attribute__((constructor))` and just `printf("%i", x);`. – Albert Jun 20 '14 at 09:31

2 Answers2

3

For types that are allowed to exist as template value parameters, you can introduce a data structure like this:

template <typename T, T K>
struct force
{
    static constexpr T value = K;
};

Usage:

force<int, std::min(S::X, 0)>::value
Travis Gockel
  • 26,877
  • 14
  • 89
  • 116
2

The constexpr is evaluated at compile time. Your problem is due to the fact that std::min is not a constexpr, so regardless of its input, the results are not a const expression (and in particular, if you initialize a variable with static lifetime using std::min, it is dynamic initialization).

The simplest solution is probably to define your own min, something along the lines of:

template <typename T>
constexpr T staticMin( T a, T b )
{
    return a > b ? b : a;
}

This should result in full evaluation at compile time, and static initialization.

James Kanze
  • 150,581
  • 18
  • 184
  • 329
  • 2
    My question is not really about `std::min`. That is just an example. I don't want to duplicate all of STL. Also, it doesn't matter that the result of `std::min` is not a const expression. I don't want/need/asked for the evaluation of `std::min` at compile time. – Albert Jun 20 '14 at 08:44
  • So what is your question? In your example code, `S::X` is a compile time constant, and it is evaluated at compile time. What isn't evaluated at compile time is `std::min`, because `std::min` isn't `constexpr`. – James Kanze Jun 20 '14 at 10:33
  • I don't care that `std::min` is not evaluated at compile time. `S::X` is not evaluated at compile time, otherwise the linker would not complain about the missing symbol. See also my linked questions, maybe they make it more clear. – Albert Jun 20 '14 at 12:24
  • Except that `S::x` _is_ evaluated at compile time. The reason the linker complains about the missing symbol is because the interface to `std::min` requires its address (indirectly, via a reference argument). (If the compiler doesn't "look into" `std::min`, it must assume that it might contain something like `&arg == &S::X`. Which must evaluate to `true`, so the compiler cannot pass a reference to a local temporary.) – James Kanze Jun 20 '14 at 12:40
  • I think we understand "evaluated at compile time" differently. See the linked questions, esp [this](http://stackoverflow.com/a/8452990/133374) answer. I basically mean that the expression of `S::X` gets inlined. As if you would have written `std::min(10, 0)`. That is the same meaning as in the linked answer. If that requires a `const T&`, it will use the address on the stack. – Albert Jun 20 '14 at 13:02
  • 2
    @Albert I think we understand "evaluate" differently. The subexpression `S::X` in the expression `std::min(S::X, 0)` is an lvalue that refers to the integer `S::X` declared as a static member in `S`. Since `std::min` accepts its arguments by reference without performing lvalue to rvalue conversion, that is what it means to evaluate `S::X` "at compile time." If you want that subexpression to be treated as a prvalue - so that the compiler creates a temporary object to pass by reference to `std::min` - it's easy enough to do so with `std::min(+S::X, 0)`. – Casey Jun 20 '14 at 13:24