Условно static_assert, если функция вызывается constexpr - PullRequest
6 голосов
/ 12 апреля 2019

Я знаю, что есть предложение для оператора constexpr (), но оно еще не реализовано в gcc / clang.Я также знаю, что есть реализация некоторых трюков, таких как редактирование машинного кода:

http://saadahmad.ca/detecting-evaluation-context-inside-constexpr-functions/

Интересно, есть ли несколько ограниченное решение для этого:

struct F {
    constexpr F(int v) {
         if constexpr(constexpr()) {
             static_assert(v > 0);
         }
         else {
             assert(v > 0);
         }
    }
};

// ...

constexpr F f{0}; // should trigger a compile-time error

Я знаю, что static_assert нельзя использовать таким образом, но это только для выяснения проблемы.

Ответы [ 2 ]

3 голосов
/ 12 апреля 2019

В вашем конкретном случае вы можете просто оставить assert - это предотвратит компиляцию, когда условие неверно, так как обработчик assert не constexpr:

#include <cassert>

struct F {
    constexpr F(int v) {
         assert(v >0);
    }
};

// ...

constexpr F f1{0}; // doesn't compile in debug
constexpr F f2{1}; // compiles

Однако это не вызовет время компиляцииошибка в выпуске.Это можно решить, сделав свой собственный assert и добавив вызов к некоторой функции non-constepxr:

#include <cassert>

// some non-constexpr function
void AssertConditionFailed()
{
}

#define ASSERT_WIH_COMPILE_TIME_CHECK(...) \
    assert(__VA_ARGS__); \
    if (!(__VA_ARGS__)) \
    { \
        AssertConditionFailed(); \
    }\

struct F {
    constexpr F(int v) {
         ASSERT_WIH_COMPILE_TIME_CHECK(v >0);
    }
};

// ...

constexpr F f1{0}; // doesn't compile
constexpr F f2{1}; // compiles
2 голосов
/ 12 апреля 2019

Не таким прямым способом, потому что static_assert там просто не будет разрешен, и аналогичным образом попытка использовать v в качестве аргумента шаблона не удастся, поэтому не нужно использовать решение типа enable_if.

В случае ошибок, если constexpr приведет к исключению, вы получите ошибку компиляции.

Вы можете использовать макрос, такой как assert (допускается начиная с C ++ 14), позволяющий ему бытьоптимизированы в сборках релизов и сохраняют исходное поведение среды отладки.

constexpr int foo(int v)
{
    if (v < 0) throw std::invalid_argument("invalid v");
    return v * 2;
}

int main() {
    int a = -1;
    int a2 = foo(a); // Not evaluated at compile time
    constexpr int b = foo(2);
    constexpr int c = foo(-1); // ERROR
}
...