Constexpr decltype [英] Constexpr decltype
问题描述
我最近在这里问了一个问题(使用SFINAE检测实例方法constexpr )其中我试图在编译时进行一些constexpr检测。最后,我发现可以利用 noexcept
来做到这一点:任何常量表达式也都是 noexcept
。所以我把下面的机制放在一起:
template< class T&
constexpr int maybe_noexcept(T& t){return 0; }
...
constexpr bool b = noexcept(maybe_noexcept(int {}));
这样工作, b
'期望,作为零初始化 int
是一个常量表达式。它也正确地产生零,当它应该(如果我改变 int
到一些其他适当的类型)。
我想检查一下是否是 constexpr
move constructible。所以我这样做:
constexpr bool b = noexcept(maybe_noexcept(int(int {})))
再次,对于 int
,或用户定义的类型。但是,这会检查类型是否具有constexpr默认构造函数和constexpr move构造函数。所以,要解决这个问题,我试图改变为declav:
constexpr bool b = noexcept int>())));
这会导致 b
在gcc 5.3.0(不能使用clang的任何,因为clang不正确地使常量表达式 noexcept
)。没有问题,我说,必须是因为 declval
是(有趣的是)没有标记 constexpr
。所以我写我自己的幼稚版本:
模板< class T&
constexpr T&& constexpr_declval()noexcept;
是的,与标准库相比,这是天真的,因为它会阻塞void,其他事情,但现在是好的。所以我再试一次:
constexpr bool b = noexcept(maybe_noexcept(int(constexpr_declval< int&
这还是不行, b
总是假的。为什么这不被认为是一个常量表达式?这是一个编译器错误,或者我不理解基本的 constexpr
?似乎在 constexpr
和未评估的上下文之间有一些奇怪的交互。
<必须定义p> constexpr
表达式。您的未定义,所以在这种情况下 int(constexpr_declval< int>())
不是 constexpr
p>
这意味着 maybe_noexcept(int(constexpr_declval< int>()))
不是 constexpr
,因此不是 noexcept
。
编译器会正确返回 false
。
您也不能在 constexpr
中调用UB。 p>
我想不出一种方法来对任意数据进行 constexpr
引用。我认为一个 constexpr
缓冲区的对齐存储重新解释为数据类型的引用,但是在许多上下文中是UB,因此不是 - constexpr
。
一般来说,这是不可能的。想象一下,你有一个类的状态确定方法调用是否 constexpr
:
struct bob {
int alice;
constexpr bob(int a = 0):alice(a){}
constexpr int get()const {
if(alice> );
return alice;
}
};
现在, bob :: get
constexpr
还是不行?这是如果你有一个 constexpr bob
构造一个非正 alice
,...它不是
您不能说假装这个值是 constexpr
,并告诉我是否有一些表达式 constexpr
。即使可以,也不能解决这个问题,因为 constexpr
参数的状态可以改变,如果一个表达式 constexpr
或不!
更有趣, bob c $ c> 是constexpr ,而
bob(1).get()
不是。所以你的第一次尝试(默认构造类型)甚至给了错误的答案:你可以测试,然后执行操作,操作将失败。
方法的一个参数,没有al参数的状态,你不能确定一个函数是否 constexpr
。
确定表达式是否 constexpr
的方法是在 constexpr
上下文中运行它,看看它是否工作。
I recently asked a question here (Detecting instance method constexpr with SFINAE) where I tried to do some constexpr detection at compile time. Eventually, I figured out that one can exploit noexcept
to do this: any constant expression is also noexcept
. So I put together the following machinery:
template <class T>
constexpr int maybe_noexcept(T && t) { return 0; }
...
constexpr bool b = noexcept(maybe_noexcept(int{}));
This works and b
is true as you'd expect, as zero-initializing an int
is a constant expression. It also correctly yields zero when it should (if I change int
to some other appropriate type).
Next, I wanted to check if something is constexpr
move constructible. So I did this:
constexpr bool b = noexcept(maybe_noexcept(int(int{})));
And again, this works properly for int
, or a user defined type. However, this checks that the type has both a constexpr default constructor and a constexpr move constructor. So, to work around this, I tried to change to declval:
constexpr bool b = noexcept(maybe_noexcept(int(declval<int>())));
This results in b
being false in gcc 5.3.0 (can't use clang for any of this, because clang does not correctly make constant expressions noexcept
). No problem, I say, must be because declval
is (interestingly enough) not marked constexpr
. So I write my own naive version:
template <class T>
constexpr T&& constexpr_declval() noexcept;
Yes, this is naive compared to how the standard library does it as it will choke on void and probably other things, but it's fine for now. So I try again:
constexpr bool b = noexcept(maybe_noexcept(int(constexpr_declval<int>())));
This still does not work, b
is always false. Why is this not considered a constant expression? Is this a compiler bug, or am I not understanding fundamental about constexpr
? It seems like there is some strange interaction between constexpr
and unevaluated contexts.
constexpr
expressions must be defined. Yours is not defined, so in that case int(constexpr_declval<int>())
is not constexpr
.
Which means maybe_noexcept(int(constexpr_declval<int>()))
is not a constexpr
, so is not noexcept
.
And the compiler properly returns false
.
You also cannot invoke UB in a constexpr
.
I cannot think of a way to make a constexpr
reference to arbitrary data. I was thinking a constexpr
buffer of aligned storage reinterpreted as a reference to the data type, but that is UB in many contexts, hence not-constexpr
.
In general, this isn't possible. Imagine you had a class whose state determines if the method call is 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, is bob::get
constexpr
or not? It is if you have a constexpr bob
constructed with a non-positive alice
, and ... it isn't if not.
You cannot say "pretend this value is constexpr
and tell me if some expression is constexpr
". Even if you could, it wouldn't solve the problem in general, because the state of a constexpr
parameter can change if an expression is constexpr
or not!
Even more fun, bob().get()
is constexpr, while bob(1).get()
is not. So your first attempt (default construct the type) even gave the wrong answer: you can test, then do the action, and the action will fail.
The object is effectively a parameter to the method, and without the state of al parameters, you cannot determine if a function is constexpr
.
The way to determine if an expression is constexpr
is to run it in a constexpr
context and see if it works.
这篇关于Constexpr decltype的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!