当std :: array是模板化函数/通用lambda的输入时,为什么它不是常量表达式? [英] Why a std::array is not constant expression when it is the input of a templated function/generic lambda?

查看:83
本文介绍了当std :: array是模板化函数/通用lambda的输入时,为什么它不是常量表达式?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

(已实现为我的问题;如果您也对此感兴趣,我将不胜感激.)

(Realted to this other question of mine; if you give a look at that too, I would really appreciate it.)

如果std::array<T,N>::sizeconstexpr ,那么为什么以下代码甚至无法编译?

If std::array<T,N>::size is constexpr, then why does the following code not even compile?

#include <array>
#include <iostream>

constexpr auto print_size = [](auto const& array){
    constexpr auto size = array.size();
    std::cout << size << '\n';
};

int main() {
    print_size(std::array<int,3>{{1,2,3}});
}

错误如下:

$ g++ -std=c++17 deleteme.cpp && ./a.out 
deleteme.cpp: In instantiation of ‘<lambda(const auto:1&)> [with auto:1 = std::array<int, 3>]’:
deleteme.cpp:10:42:   required from here
deleteme.cpp:5:20: error: ‘array’ is not a constant expression
    5 |     constexpr auto size = array.size();
      |                    ^~~~

但我不知道为什么.

在lambda调用站点上,参数在编译时是已知的,并且lambda应该用等于std::array<int,3>auto实例化,其中3是编译时间值,因此应输出array.size().

At the lambda call site, the argument is known at compile time, and the lambda should be instantiated with auto equal to std::array<int,3>, where 3 is a compile time value, and so should be output of array.size().

我的推理有什么问题?

顺便说一句,如果我使用模板化函数而不是通用lambda,则同样适用.

By the way, the same holds if I use a templated function instead of the generic lambda.

推荐答案

问题是 [expr.const]/5.12 :

5-表达式E是核心常量表达式,除非按照抽象机([intro.execution])的规则对E的求值将对以下值之一进行求值: [...]

5 - An expression E is a core constant expression unless the evaluation of E, following the rules of the abstract machine ([intro.execution]), would evaluate one of the following: [...]

  • (5.12)引用引用类型的变量或数据成员的id表达式,除非引用具有先前的初始化并且
    • (5.12.1)可用于常量表达式或
    • (5.12.2)其寿命始于E的评估;
    • (5.12) an id-expression that refers to a variable or data member of reference type unless the reference has a preceding initialization and either
      • (5.12.1) it is usable in constant expressions or
      • (5.12.2) its lifetime began within the evaluation of E;

      由于变量array是引用,因此即使评估实际上没有任何作用,也不允许对其进行评估(在表达式array.size()内部).

      Since the variable array is a reference, it is not permitted to evaluate it (inside the expression array.size()), even though the evaluation doesn't actually do anything.

      通过值(const或非const)传递array使代码有效:

      Passing array by value (const or non-const) makes the code valid:

      constexpr auto print_size = [](auto const array){
          constexpr auto size = array.size(); // ok
          std::cout << size << '\n';
      };
      

      但是引用该参数并在下一行使用它是无效的:

      But taking a reference to that parameter and using it on the very next line is invalid:

      constexpr auto print_size = [](auto const arr){
          auto const& array = arr;
          constexpr auto size = array.size(); // error
          std::cout << size << '\n';
      };
      

      请注意,gcc 9错误地接受了此代码;只是从版本10开始,gcc才得到了正确的答案.

      Note that gcc 9 incorrectly accepts this code; it is only since version 10 that gcc gets this correct.

      gcc 10在相关领域仍不符合要求;它接受在引用上调用static constexpr成员函数. 使用constexpr静态成员作为模板参数的引用这是不正确的,并且clang正确拒绝了它:

      gcc 10 still is noncompliant in a related area; it accepts calling a static constexpr member function on a reference. Using a constexpr static member of a reference as template argument This is incorrect and clang correctly rejects it:

      struct S { static constexpr int g() { return 1; } };
      void f(auto const& s) {
          constexpr auto x = s.g(); // error
          constexpr auto y = decltype(s)::g(); // ok
      }
      int main() { f(S{}); }
      

      这篇关于当std :: array是模板化函数/通用lambda的输入时,为什么它不是常量表达式?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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