Home > Software engineering >  how to evaluate concept to false upon expression compilation error
how to evaluate concept to false upon expression compilation error

Time:05-08

I was trying to change the following example concept code that, under certain inputs, caused an error instead of evaluating false:

template <typename T>
constexpr bool inner = T::prop;

template <typename T>
concept outer = inner<T>;

struct pass {static constexpr bool prop = true;};
struct fail {static constexpr bool prop = false;};
struct bad_fail {static constexpr std::tuple<> prop{};};

static_assert(outer<pass>);
static_assert(not outer<fail>);
static_assert(not outer<bad_fail>);

This has compile error cannot convert 'const std::tuple<>' to 'const bool'.

I attempted to only evaluate inner when it would compile using a requires clause:

template <typename T>
constexpr bool inner = T::prop;

template <typename T>
concept outer = []() consteval -> bool {
    if constexpr (
        requires() {{
            inner<T>
        } -> std::same_as<const bool&>;}
    ) {
        return inner<T>;
    } else {
        return false;
    }
}();

struct pass {static constexpr bool prop = true;};
struct fail {static constexpr bool prop = false;};
struct bad_fail {static constexpr std::tuple<> prop{};};

static_assert(outer<pass>);
static_assert(not outer<fail>);
static_assert(not outer<bad_fail>);

This results in a confusing error:

error: non-constant condition for static assertion ... static_assert(not outer<bad_fail>)

And the same cannot convert 'const std::tuple<>' to 'const bool' which appears to be from the 2nd inner. (The compiler does not specify)

I then attempted to combine the compilation check and truth check into a static_assert check within a requires clause:

template <typename T>
constexpr bool inner = T::prop;

template <typename T>
concept outer = requires() {{
    []() constexpr {
        static_assert(inner<T>);
    }()
} -> std::same_as<void>;};

struct pass {static constexpr bool prop = true;};
struct fail {static constexpr bool prop = false;};
struct bad_fail {static constexpr std::tuple<> prop{};};

static_assert(outer<pass>);
static_assert(not outer<fail>);
//static_assert(not outer<bad_fail>);

This results in a confusing error:

error: static assertion failed ... static_assert(not outer<fail>)

This is confusing because:

error: static assertion failed ... static_assert(inner<T>)

Is also thrown, implying that static_assert escapes requires clauses. Which sure enough is true:

template <std::monostate>
concept should_false = requires() {{
    []() constexpr {
        static_assert(false);
    }()
} -> std::same_as<void>;};

As this has compile error error: static assertion failed ... static_assert(false)!

How can I have outer<bad_fail> evaluate to false with no compilation errors?

CodePudding user response:

You can use requires-clause to initialize inner, which first requires that the return type of T::prop must be const bool&, then use nested requires with T::prop as its value

#include <concepts>

template <typename T>
constexpr bool inner = requires { 
  {T::prop} -> std::same_as<const bool&>;
  requires T::prop;
};

template <typename T>
concept outer = inner<T>;

Demo

  • Related