Проверка времени компиляции И проверка времени выполнения "одновременно"

Предположим, у меня есть следующая упрощенная программа:

Ссылка на godbolt.org:

#include <cassert>

struct Dimensions {

    Dimensions& operator=(int i) {
      assert(i != 0);
      return *this;
    }

};

int getDim();

int main() {
    Dimensions dims;
    dims = getDim();//ok, just use runtime assert
    dims = 0;//compile error wanted here
    return 0;
}

В первом случае (getDim) невозможно проверить время компиляции, поэтому мы рады просто проверить его во время выполнения.

Но возможно ли как-то обнаружить и при компиляции (для второго случая, dims = 0;), когда в теории это выглядит так, как будто это возможно? (возможно с какой-то перегрузкой или даже оберткой?)

Ответ 1

Типичный способ сделать это в C с помощью компилятора gcc, который также будет работать в C++: вы используете встроенный __builtin_constant_p для проверки, является ли выражение постоянно вычисляемым, затем проверяете выражение и затем вызываете функцию, объявленную с помощью __attribute__((__warning__)) или с помощью __attribute__((__error__)). Вот так:

#include <cassert>
#include <type_traits>

#define CONCAT(a, b) a ## b
#define XCONCAT(a, b) CONCAT(a, b)
#define maybe_static_maybe_not_assert(expr) do { \
    if (__builtin_constant_p(expr)) { \
            if (!(expr)) { \
                extern __attribute__((__warning__( \
                "static_assert: expression: " #expr " will fail on runtime!" \
                ))) void XCONCAT(maybe_static_maybe_not_assert_warn, __LINE__)(); \
              XCONCAT(maybe_static_maybe_not_assert_warn, __LINE__)(); \
         } \
    } \
    assert(expr); \
} while(0)

struct Dimensions {

    Dimensions& operator=(int i) {
        maybe_static_maybe_not_assert(i != 0);
        return *this;
    }

};

int getDim();

int main() {
    Dimensions dims;
    dims = getDim();
    dims = 0;
    dims = 1;
    return 0;
}

При компиляции с оптимизацией должно выдаваться предупреждение:

In member function 'Dimensions& Dimensions::operator=(int)',
    inlined from 'int main()' at <source>:32:12:
<source>:12:70: warning: call to 'maybe_static_maybe_not_assert_warn21' declared with attribute warning: static_assert: expression: i != 0 will fail on runtime! [-Wattribute-warning]
   12 |                 XCONCAT(maybe_static_maybe_not_assert_warn, __LINE__)(); \
      |                                                                      ^
<source>:21:9: note: in expansion of macro 'maybe_static_maybe_not_assert'
   21 |         maybe_static_maybe_not_assert(i != 0);
      |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Compiler returned: 0

Именно так _FORTIFY_SOURCE реализован в glibc.