constexpr decltype
Constexpr decltype
我最近在这里 (Detecting instance method constexpr with SFINAE) 问了一个问题,我试图在编译时做一些 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
可移动构造。所以我这样做了:
constexpr bool b = noexcept(maybe_noexcept(int(int{})));
同样,这适用于 int
或用户定义的类型。但是,这会检查该类型是否同时具有 constexpr 默认构造函数和 constexpr 移动构造函数。因此,为了解决这个问题,我尝试更改为 declval:
constexpr bool b = noexcept(maybe_noexcept(int(declval<int>())));
这导致 b
在 gcc 5.3.0 中为 false(不能使用 clang 来处理这些,因为 clang 不能正确地生成常量表达式 noexcept
)。没问题,我说,一定是因为 declval
(很有趣)没有标记为 constexpr
。所以我写了我自己的幼稚版本:
template <class T>
constexpr T&& constexpr_declval() noexcept;
是的,与标准库的处理方式相比,这是幼稚的,因为它会因 void 和其他可能的原因而窒息,但目前还好。所以我再试一次:
constexpr bool b = noexcept(maybe_noexcept(int(constexpr_declval<int>())));
这还是不行,b
总是假的。为什么这不被视为常量表达式?这是编译器错误,还是我不了解 constexpr
的基础知识? constexpr
和未评估的上下文之间似乎存在一些奇怪的相互作用。
constexpr
表达式必须定义。你的没有定义,所以在那种情况下 int(constexpr_declval<int>())
不是 constexpr
.
这意味着 maybe_noexcept(int(constexpr_declval<int>()))
不是 constexpr
,因此也不是 noexcept
。
并且编译正常returnsfalse
.
您也不能在 constexpr
中调用 UB。
我想不出对任意数据进行 constexpr
引用的方法。我在考虑将对齐存储的 constexpr
缓冲区重新解释为对数据类型的引用,但在许多情况下这是 UB,因此不是-constexpr
.
一般来说,这是不可能的。假设您有一个 class,其状态决定方法调用是否为 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;
}
};
现在,是bob::get
constexpr
还是不是?如果你有一个 constexpr bob
用非正数 alice
构造的,并且......如果不是,则不是。
你不能说 "pretend this value is constexpr
and tell me if some expression is constexpr
"。即使可以,它也不能解决一般问题,因为 constexpr
参数的 state 可以根据表达式是否为 constexpr
而改变!
更有趣的是,bob().get()
是 constexpr,而 bob(1).get()
不是。所以你的第一次尝试(默认构造类型)甚至给出了错误的答案:你可以测试,然后执行操作,操作将失败。
对象实际上是方法的参数,没有所有参数的状态,您无法确定函数是否 constexpr
。
确定表达式是否为 constexpr
的方法是 运行 在 constexpr
上下文中查看它是否有效。
我最近在这里 (Detecting instance method constexpr with SFINAE) 问了一个问题,我试图在编译时做一些 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
可移动构造。所以我这样做了:
constexpr bool b = noexcept(maybe_noexcept(int(int{})));
同样,这适用于 int
或用户定义的类型。但是,这会检查该类型是否同时具有 constexpr 默认构造函数和 constexpr 移动构造函数。因此,为了解决这个问题,我尝试更改为 declval:
constexpr bool b = noexcept(maybe_noexcept(int(declval<int>())));
这导致 b
在 gcc 5.3.0 中为 false(不能使用 clang 来处理这些,因为 clang 不能正确地生成常量表达式 noexcept
)。没问题,我说,一定是因为 declval
(很有趣)没有标记为 constexpr
。所以我写了我自己的幼稚版本:
template <class T>
constexpr T&& constexpr_declval() noexcept;
是的,与标准库的处理方式相比,这是幼稚的,因为它会因 void 和其他可能的原因而窒息,但目前还好。所以我再试一次:
constexpr bool b = noexcept(maybe_noexcept(int(constexpr_declval<int>())));
这还是不行,b
总是假的。为什么这不被视为常量表达式?这是编译器错误,还是我不了解 constexpr
的基础知识? constexpr
和未评估的上下文之间似乎存在一些奇怪的相互作用。
constexpr
表达式必须定义。你的没有定义,所以在那种情况下 int(constexpr_declval<int>())
不是 constexpr
.
这意味着 maybe_noexcept(int(constexpr_declval<int>()))
不是 constexpr
,因此也不是 noexcept
。
并且编译正常returnsfalse
.
您也不能在 constexpr
中调用 UB。
我想不出对任意数据进行 constexpr
引用的方法。我在考虑将对齐存储的 constexpr
缓冲区重新解释为对数据类型的引用,但在许多情况下这是 UB,因此不是-constexpr
.
一般来说,这是不可能的。假设您有一个 class,其状态决定方法调用是否为 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;
}
};
现在,是bob::get
constexpr
还是不是?如果你有一个 constexpr bob
用非正数 alice
构造的,并且......如果不是,则不是。
你不能说 "pretend this value is constexpr
and tell me if some expression is constexpr
"。即使可以,它也不能解决一般问题,因为 constexpr
参数的 state 可以根据表达式是否为 constexpr
而改变!
更有趣的是,bob().get()
是 constexpr,而 bob(1).get()
不是。所以你的第一次尝试(默认构造类型)甚至给出了错误的答案:你可以测试,然后执行操作,操作将失败。
对象实际上是方法的参数,没有所有参数的状态,您无法确定函数是否 constexpr
。
确定表达式是否为 constexpr
的方法是 运行 在 constexpr
上下文中查看它是否有效。