为什么非 constexpr std::integral_constant 用作模板参数?

use*_*445 6 c++ templates constexpr

我的问题是为什么以下代码是有效的 C++:

#include <iostream>
#include <tuple>
#include <type_traits>

std::tuple<const char *, const char *> tuple("Hello", "world");

std::integral_constant<std::size_t, 0> zero;
std::integral_constant<std::size_t, 1> one;

template<typename T> const char *
lookup(T n)
{
  // I would expect to have to write this:
  //   return std::get<decltype(n)::value>(tuple);

  // But actually this works:
  return std::get<n>(tuple);
}

int
main()
{
  std::cout << lookup(zero) << " " << lookup(one) << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

当然,我很高兴能够以这种方式编程。而且,我知道std::integral_constant有一个constexpr转换运算符。但是,参数ntolookup不是 constexpr,因此我对非 constexpr 对象上的非静态方法(即使方法本身是 constexpr)如何可能返回编译时常量感到困惑。

当然,在这种情况下我们碰巧知道转换运算符的主体不查看运行时值,但类型签名中没有任何内容保证这一点。例如,以下类型显然不起作用,尽管它也有 constexpr 转换运算符:

struct bad_const {
  const std::size_t value;
  constexpr bad_const(std::size_t v) : value(v) {}
  constexpr operator std::size_t() const noexcept { return value; }
};

bad_const badone(1);
Run Code Online (Sandbox Code Playgroud)

如果方法忽略隐式参数,是否有一些额外的属性会被视为不同的this

Bri*_*ian 7

当然,在这种情况下我们碰巧知道转换运算符的主体不查看运行时值,但类型签名中没有任何内容保证这一点。

正确的。你所缺少的是这样一个事实:这并不重要。

当在常量表达式求值期间调用函数时,编译器会检查该函数是否为constexpr. 如果不是constexpr,则封闭的求值无法产生常量表达式。

但如果是 constexpr则编译器在编译时执行其主体,并检查其主体中的任何内容是否使封闭的评估不符合常量表达式的资格。

因此,在 的转换运算符的情况下std::integral_constant,编译器在编译时执行其主体,发现它只是返回模板参数的值,这很好。

如果编译器正在评估constexpr某种其他类型的成员函数,该函数读取const对象的非非静态数据成员(并且该对象不是constexpr),那么此时编译器将确定封闭的评估不是一个常量表达式。

有点令人沮丧的是,当您看到一个已声明的函数时constexpr,这并没有告诉您该函数的哪些计算会产生常量表达式,但事实就是如此。