为什么此嵌套lambda不被视为constexpr? [英] Why is this nested lambda not considered constexpr?

查看:77
本文介绍了为什么此嵌套lambda不被视为constexpr?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用嵌套的constexpr lambdas创建一个咖喱接口,但是编译器并不认为它是一个常量表达式.

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);
}

我早些时候发布了一个问题,因为我发现了一个不同的最小示例,但这还不够.

错误:

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:如果有人可以向我解释此错误,那将是一个很大的帮助.我之前曾遇到该错误,但不记得为什么.

解决方案

问题是您试图在模板非类型参数中使用λ捕获的变量之一.

  return hana::type_c<Array<typename decltype(type)::type, size()>>;
//                                                         ^~~~

模板非类型参数必须是一个常量表达式.在lambda内,您不能在常量表达式中使用捕获的变量. λ是否为constexpr无关紧要.

但是您可以在常量表达式中使用普通变量,即使它们不是constexpr变量也是如此.例如,这是合法的:

std::integral_constant<int, 100> i; // i is not constexpr
std::array<int, i()> a; // using i in a constant expression

那么为什么我们不能在常量表达式中使用捕获的变量呢?我不知道此规则的动机,但在标准中是这样的:

[expr.const]

(¶ 2)条件表达式核心常量表达式,除非... (¶ 2.11)在 lambda-expression 中,是对this的引用,或者是对在 lambda-expression 之外定义的具有自动存储期限的变量的引用.成为奇特用途.

CWG1613 可能有些线索

如果我们将内部lambda重写为命名类,则会遇到一个不同但相关的问题:

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指针"进行间接调用. /p>

这是对代码进行最少改动的解决方法:

constexpr auto array_ = [] (auto size) {
  return [=] (auto type) {
    const auto size_ = size;
    return hana::type_c<Array<typename decltype(type)::type, size_()>>;
  };
};

现在,我们在常量表达式之外使用捕获的变量来初始化一个普通变量,然后可以在模板非类型参数中使用它.

此答案已经过几次编辑,因此下面的注释可能引用以前的版本.

I'm trying to create a curried interface using nested constexpr lambdas, but the compiler does not consider it to be a constant expression.

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);
}

I post a question earlier because I found a different minimal example, but it wasn't enough.

Error:

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 : if someone could explain me this error that would be a great help. I ran into that error before but can't remember why.

解决方案

The problem is you are trying to odr-use one of a lambda's captured variables in a template non-type argument.

  return hana::type_c<Array<typename decltype(type)::type, size()>>;
//                                                         ^~~~

A template non-type argument must be a constant expression. Inside a lambda, you can't odr-use a captured variable in a constant expression. Whether or not the lambda is constexpr is irrelevant.

But you can odr-use ordinary variables in constant expressions, even if they are not constexpr variables. For example, this is legal:

std::integral_constant<int, 100> i; // i is not constexpr
std::array<int, i()> a; // using i in a constant expression

So why can't we odr-use captured variables in constant expressions? I don't know the motivation for this rule, but here it is in the standard:

[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 may hold some clue.

If we were to rewrite the inner lambda as a named class, we would have a different but related problem:

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 };
};

Now the error would be the implicit use of the this pointer in a template non-type argument.

  return hana::type_c<Array<typename decltype(type)::type, size()>>;
//                                                         ^~~~~

I declared Closure::operator()() as a constexpr function for consistency, but that is immaterial. The this pointer is forbidden to be used in a constant expression ([expr.const] ¶2.1). Functions declared constexpr do not get special dispensation to relax the rules for the constant expressions that may appear within them.

Now the original error makes a little bit more sense, because captured variables are transformed into data members of the lambda's closure type, so using captured variables is a little bit like indirecting through the lambda's own "this pointer".

This is the workaround that introduces the least alteration to the code:

constexpr auto array_ = [] (auto size) {
  return [=] (auto type) {
    const auto size_ = size;
    return hana::type_c<Array<typename decltype(type)::type, size_()>>;
  };
};

Now we are using the captured variable outside of a constant expression, to initialize an ordinary variable which we can then use in the template non-type argument.

This answer has been edited a few times, so the comments below may reference previous revisions.

这篇关于为什么此嵌套lambda不被视为constexpr?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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