user10133158
user10133158

Reputation:

C++ How to convince constexpr function to be constexpr, for any kind of argument

I could not find a relevant question/answer for this one. Consider this:

// constexpr declares intent
template <typename T> inline constexpr const bool probe (T const &) noexcept { return false; }
template <typename T> inline constexpr const bool probe (T const *) noexcept { return true; }
template <typename T> inline constexpr const bool probe (T &&) noexcept = delete ;
template <typename T> inline constexpr const bool probe (T) noexcept = delete ;

As we all know and expect the following do work as expected, at compile time:

constexpr inline const char * holla_ = "Hola!";
// OK
static_assert(probe(holla_));
// OK
static_assert(probe("string literal"));

And these too:

inline const char buff[]{"ABCD"};
// OK -- although `buff` is not compile time array
static_assert(probe(buff));

constexpr inline int const * ip = nullptr ;
static_assert(probe(ip));

But here is the, compile time no-can-do area:

// deliberately omitted constexpr
inline const char * wot_here_ = "Another literal";

// error: the value of 'wot_here_' is not usable in a 
// constant expression
// note: 'wot_here_' was not declared 'constexpr'
// static_assert(probe(wot_here_));

I understand wot_here_ is the runtime variable. probe() is declared and implemented with argument types only. Am I openly going against some obvious rule in the standard? Or subtly, against a few subtle ones.

I am cautiously hoping someone can sort of, "get around" this issue?

Code is here

Upvotes: 2

Views: 683

Answers (2)

xskxzr
xskxzr

Reputation: 13040

A workaround is to declare the pointer parameter of probe as a reference such that no lvalue-to-rvalue conversion is required:

template <typename T> inline constexpr const bool probe (T const * &) noexcept { return true;  }
                                                                // ^
// this one is required to accept holla_, which is of type char const * const
template <typename T> inline constexpr const bool probe (T const * const &) noexcept { return true;  }

// this one is required to accept buff, which is an array
template <typename T, std::size_t N> inline constexpr const bool probe (T const (&)[N]) noexcept { return true;  }

// the other three overloads keep the same
template <typename T> inline constexpr const bool probe (T const &) noexcept { return false; }
template <typename T> inline constexpr const bool probe (T &&) noexcept = delete ;
template <typename T> inline constexpr const bool probe (T) noexcept = delete ;

Upvotes: 1

max66
max66

Reputation: 66230

I am really hoping someone can sort-of-a, "get around" this issue?

The only "get around" I see is declate wot_her_ constexpr.

If you define

inline const char * wot_here_ = "Another literal";

you have a variable that is initialized run-time.

Observe that const char * is a variable pointer to a constant char, so isn't a constant value because you can incerement/decrement it.

A constexpr function can be also called by a run-time variable, so you can call

probe( wot_here_)

but probe(), in this case, is executed run-time.

The problem is that static_assert() is executed necessarily compile-time, so

static_assert( probe( wot_here_) );

gives error because the compiler can't check compile time something that is executed run-time.

The only solution that I see is define wot_here_ constexpr, as holla_ before, so the compiler can execute compile-time the probe(wot_here_) inside the static_assert().

Upvotes: 2

Related Questions