Constexpr decltype

I recently asked a question here ( Detecting a constexpr instance method using SFINAE ), where I tried to do some constexpr detection at compile time. In the end, I realized that noexcept can be used for this: any constant expression is also noexcept . So I put together the following technique:

 template <class T> constexpr int maybe_noexcept(T && t) { return 0; } ... constexpr bool b = noexcept(maybe_noexcept(int{})); 

This works, and b true, as you would expect, since initializing zero to int is a constant expression. It also correctly returns zero when it should (if I change int to some other suitable type).

Next, I wanted to check that something constexpr moving constructively. So I did this:

 constexpr bool b = noexcept(maybe_noexcept(int(int{}))); 

And again, this works correctly for an int or user-defined type. However, this verifies that the type has both a default constexpr constructor and a constexpr move constructor. So, to get around this, I tried switching to declval:

 constexpr bool b = noexcept(maybe_noexcept(int(declval<int>()))); 

This causes b be false in gcc 5.3.0 (cannot use clang for any of this, because clang does noexcept constant expressions noexcept ). Not a problem, I say, it should be because declval (interestingly) is not marked constexpr . Therefore, I am writing my naive version:

 template <class T> constexpr T&& constexpr_declval() noexcept; 

Yes, this is naive compared to the way the standard library does, as it will sweep the void and possibly other things, but so far it is beautiful. So I will try again:

 constexpr bool b = noexcept(maybe_noexcept(int(constexpr_declval<int>()))); 

This still does not work, b always false. Why is this not considered a constant expression? Is this a compiler error, or do I not understand the fundamental information about constexpr ? There seems to be some kind of weird interaction between constexpr and invaluable contexts.

+5
source share
1 answer

constexpr must be defined. Yours is not defined, so in this case int(constexpr_declval<int>()) not constexpr .

Which means maybe_noexcept(int(constexpr_declval<int>())) not constexpr , therefore not noexcept .

And the compiler correctly returns false .

You also cannot invoke UB in constexpr .

I can't think of a way to link constexpr to arbitrary data. I thought that the constexpr buffer of the aligned storage was reinterpreted as a reference to the data type, but it is UB in many contexts, therefore, not- constexpr .

In general, this is not possible. Imagine that you had a class whose state determines whether the call to the constexpr method constexpr :

 struct bob { int alice; constexpr bob(int a=0):alice(a) {} constexpr int get() const { if (alice > 0) throw std::string("nope"); return alice; } }; 

now, bob::get constexpr or not? That is if you have a constexpr bob built with non-positive alice , and ... it is not.

You cannot say "pretend that it is a constexpr value and tell me if some expression is constexpr ". Even if you could, this would not solve the problem at all, because the state of the constexpr parameter can change if the expression is constexpr or not!

Even more fun, bob().get() is constexpr, while bob(1).get() is not. So, your first attempt (by default to build the type) even gave the wrong answer: you can test, then perform the action, and the action will fail.

An object is effectively a parameter of a method and without the state of all parameters, you cannot determine if there is a constexpr function.

A way to determine if a constexpr expression is to run it in the context of constexpr and see if it works.

+6
source

Source: https://habr.com/ru/post/1242558/


All Articles