为什么这个嵌套的 lambda 不被认为是 constexpr?
Why is this nested lambda not considered constexpr?
我正在尝试使用嵌套的 constexpr lambda 创建柯里化接口,但编译器不认为它是常量表达式。
namespace hana = boost::hana;
using namespace hana::literals;
struct C1 {};
template < typename T,
std::size_t size >
struct Array {};
constexpr auto array_ = [] (auto size) {
return [=] (auto type) {
return hana::type_c<Array<typename decltype(type)::type, size()>>;
};
};
int main() {
constexpr auto c1 = hana::type_c<C1>;
constexpr auto test = hana::type_c<Array<typename decltype(c1)::type, hana::size_c<100>()>>;
constexpr auto test2 = array_(hana::size_c<100>)(c1);
}
我之前 post 一个问题,因为我找到了一个不同的最小示例,但这还不够。
错误:
test2.cpp: In instantiation of ‘<lambda(auto:1)>::<lambda(auto:2)> [with auto:2 = boost::hana::type_impl<C1>::_; auto:1 = boost::hana::integral_constant<long unsigned int, 100>]’:
test2.cpp:31:54: required from here
test2.cpp:20:16: error: ‘__closure’ is not a constant expression
return hana::type_c<Array<typename decltype(type)::type, size()>>;
^~~~
test2.cpp:20:16: note: in template argument for type ‘long unsigned int’
test2.cpp: In function ‘int main()’:
test2.cpp:31:18: error: ‘constexpr const void test2’ has incomplete type
constexpr auto test2 = array_(hana::size_c<100>)(c1);
__closure is not a constant expression
:如果有人能向我解释这个错误,那将是一个很大的帮助。我 运行 以前遇到过那个错误,但不记得为什么了。
我将您的测试用例缩减为:
#include <type_traits>
constexpr auto f = [](auto size) {
return [=](){
constexpr auto s = size();
return 1;
};
};
static_assert(f(std::integral_constant<int, 100>{})(), "");
int main() { }
如上面评论所述,发生这种情况是因为 size
不是函数体内的常量表达式。这不是 Hana 特有的。作为解决方法,您可以使用
constexpr auto f = [](auto size) {
return [=](){
constexpr auto s = decltype(size)::value;
return 1;
};
};
或类似的东西。
问题是您正在尝试在模板非类型参数中 odr-use lambda 捕获的变量之一。
return hana::type_c<Array<typename decltype(type)::type, size()>>;
// ^~~~
模板非类型参数必须是常量表达式。在 lambda 内部,您不能在常量表达式中 odr-use 捕获的变量。 lambda 是否为 constexpr
无关紧要。
但是您可以在常量表达式中 odr-使用普通变量,即使它们不是 constexpr
变量。例如,这是合法的:
std::integral_constant<int, 100> i; // i is not constexpr
std::array<int, i()> a; // using i in a constant expression
那么为什么我们不能在常量表达式中 odr-use 捕获的变量呢?我不知道这个规则的动机,但它是在标准中:
[expr.const]
(¶2) A conditional-expression is a core constant expression unless...
(¶2.11) in a lambda-expression, a reference to this
or to a variable with automatic storage duration defined outside that lambda-expression, where the reference would be an odr-use.
CWG1613 可能有一些线索。
如果我们将内部 lambda 重写为命名 class,我们将遇到一个不同但相关的问题:
template <typename T>
struct Closure {
T size;
constexpr Closure(T size_) : size(size_) {}
template <typename U>
constexpr auto operator()(U type) const {
return hana::type_c<Array<typename decltype(type)::type, size()>>;
}
};
constexpr auto array_ = [] (auto size) {
return Closure { size };
};
现在的错误是在模板非类型参数中隐式使用 this
指针。
return hana::type_c<Array<typename decltype(type)::type, size()>>;
// ^~~~~
为了保持一致性,我将 Closure::operator()()
声明为 constexpr
函数,但这并不重要。禁止在常量表达式中使用 this
指针([expr.const] ¶2.1)。声明的函数 constexpr
未获得特殊豁免以放宽可能出现在其中的常量表达式的规则。
现在原来的错误更有意义了,因为捕获的变量被转换为 lambda 闭包类型的数据成员,所以使用捕获的变量有点像通过 lambda 自己的间接“this
指针”。
这是对代码进行最少更改的解决方法:
constexpr auto array_ = [] (auto size) {
return [=] (auto type) {
const auto size_ = size;
return hana::type_c<Array<typename decltype(type)::type, size_()>>;
};
};
现在我们在常量表达式之外使用捕获的变量来初始化一个普通变量,然后我们可以在模板非类型参数中使用它。
这个答案已经过几次编辑,所以下面的评论可能会参考以前的修订。
我正在尝试使用嵌套的 constexpr lambda 创建柯里化接口,但编译器不认为它是常量表达式。
namespace hana = boost::hana;
using namespace hana::literals;
struct C1 {};
template < typename T,
std::size_t size >
struct Array {};
constexpr auto array_ = [] (auto size) {
return [=] (auto type) {
return hana::type_c<Array<typename decltype(type)::type, size()>>;
};
};
int main() {
constexpr auto c1 = hana::type_c<C1>;
constexpr auto test = hana::type_c<Array<typename decltype(c1)::type, hana::size_c<100>()>>;
constexpr auto test2 = array_(hana::size_c<100>)(c1);
}
我之前 post 一个问题,因为我找到了一个不同的最小示例,但这还不够。
错误:
test2.cpp: In instantiation of ‘<lambda(auto:1)>::<lambda(auto:2)> [with auto:2 = boost::hana::type_impl<C1>::_; auto:1 = boost::hana::integral_constant<long unsigned int, 100>]’:
test2.cpp:31:54: required from here
test2.cpp:20:16: error: ‘__closure’ is not a constant expression
return hana::type_c<Array<typename decltype(type)::type, size()>>;
^~~~
test2.cpp:20:16: note: in template argument for type ‘long unsigned int’
test2.cpp: In function ‘int main()’:
test2.cpp:31:18: error: ‘constexpr const void test2’ has incomplete type
constexpr auto test2 = array_(hana::size_c<100>)(c1);
__closure is not a constant expression
:如果有人能向我解释这个错误,那将是一个很大的帮助。我 运行 以前遇到过那个错误,但不记得为什么了。
我将您的测试用例缩减为:
#include <type_traits>
constexpr auto f = [](auto size) {
return [=](){
constexpr auto s = size();
return 1;
};
};
static_assert(f(std::integral_constant<int, 100>{})(), "");
int main() { }
如上面评论所述,发生这种情况是因为 size
不是函数体内的常量表达式。这不是 Hana 特有的。作为解决方法,您可以使用
constexpr auto f = [](auto size) {
return [=](){
constexpr auto s = decltype(size)::value;
return 1;
};
};
或类似的东西。
问题是您正在尝试在模板非类型参数中 odr-use lambda 捕获的变量之一。
return hana::type_c<Array<typename decltype(type)::type, size()>>;
// ^~~~
模板非类型参数必须是常量表达式。在 lambda 内部,您不能在常量表达式中 odr-use 捕获的变量。 lambda 是否为 constexpr
无关紧要。
但是您可以在常量表达式中 odr-使用普通变量,即使它们不是 constexpr
变量。例如,这是合法的:
std::integral_constant<int, 100> i; // i is not constexpr
std::array<int, i()> a; // using i in a constant expression
那么为什么我们不能在常量表达式中 odr-use 捕获的变量呢?我不知道这个规则的动机,但它是在标准中:
[expr.const]
(¶2) A conditional-expression is a core constant expression unless... (¶2.11) in a lambda-expression, a reference to
this
or to a variable with automatic storage duration defined outside that lambda-expression, where the reference would be an odr-use.
CWG1613 可能有一些线索。
如果我们将内部 lambda 重写为命名 class,我们将遇到一个不同但相关的问题:
template <typename T>
struct Closure {
T size;
constexpr Closure(T size_) : size(size_) {}
template <typename U>
constexpr auto operator()(U type) const {
return hana::type_c<Array<typename decltype(type)::type, size()>>;
}
};
constexpr auto array_ = [] (auto size) {
return Closure { size };
};
现在的错误是在模板非类型参数中隐式使用 this
指针。
return hana::type_c<Array<typename decltype(type)::type, size()>>;
// ^~~~~
为了保持一致性,我将 Closure::operator()()
声明为 constexpr
函数,但这并不重要。禁止在常量表达式中使用 this
指针([expr.const] ¶2.1)。声明的函数 constexpr
未获得特殊豁免以放宽可能出现在其中的常量表达式的规则。
现在原来的错误更有意义了,因为捕获的变量被转换为 lambda 闭包类型的数据成员,所以使用捕获的变量有点像通过 lambda 自己的间接“this
指针”。
这是对代码进行最少更改的解决方法:
constexpr auto array_ = [] (auto size) {
return [=] (auto type) {
const auto size_ = size;
return hana::type_c<Array<typename decltype(type)::type, size_()>>;
};
};
现在我们在常量表达式之外使用捕获的变量来初始化一个普通变量,然后我们可以在模板非类型参数中使用它。
这个答案已经过几次编辑,所以下面的评论可能会参考以前的修订。