Constexpr decltype [英] Constexpr decltype

查看:165
本文介绍了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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆