为什么C ++编译器可以将函数声明为constexpr,而不能将其声明为constexpr?

Ale*_*lex 4 c++ templates compile-time constexpr c++11

为什么C ++编译器可以将函数声明为constexpr,而不能将其声明为constexpr?

例如:http : //melpon.org/wandbox/permlink/AGwniRNRbfmXfj8r

#include <iostream>
#include <functional>
#include <numeric>
#include <initializer_list>

template<typename Functor, typename T, size_t N>
T constexpr reduce(Functor f, T(&arr)[N]) {
  return std::accumulate(std::next(std::begin(arr)), std::end(arr), *(std::begin(arr)), f);
}

template<typename Functor, typename T>
T constexpr reduce(Functor f, std::initializer_list<T> il) {
  return std::accumulate(std::next(il.begin()), il.end(), *(il.begin()), f);
}

template<typename Functor, typename T, typename... Ts>
T constexpr reduce(Functor f, T t1, Ts... ts) {
  return f(t1, reduce(f, std::initializer_list<T>({ts...})));
}

int constexpr constexpr_func() { return 2; }

template<int value>
void print_constexpr() { std::cout << value << std::endl; }

int main() {
  std::cout << reduce(std::plus<int>(), 1, 2, 3, 4, 5, 6, 7) << std::endl;  // 28
  std::cout << reduce(std::plus<int>(), {1, 2, 3, 4, 5, 6, 7}) << std::endl;// 28

  const int input[3] = {1, 2, 3};   // 6
  std::cout << reduce(std::plus<int>(), input) << std::endl;

  print_constexpr<5>(); // OK
  print_constexpr<constexpr_func()>();  // OK
  //print_constexpr<reduce(std::plus<int>(), {1, 2, 3, 4, 5, 6, 7})>(); // error 

  return 0;
}
Run Code Online (Sandbox Code Playgroud)

输出:

28
28
6
5
2
Run Code Online (Sandbox Code Playgroud)

为什么在此行出现错误://print_constexpr<reduce(std::plus<int>(), {1, 2, 3, 4, 5, 6, 7})>(); // error 即使对于C ++ 14和C ++ 1z也是如此

为什么编译器允许将标记reduce()constexpr,但是reduce()即使所有参数reduce()在编译时都已传递给已知参数,也不能用作模板参数?


对于某些编译器而言,效果相同- 支持C ++ 14 -std=c++14

对于所有这些情况,请编译确定,直到未使用的行为止: //print_constexpr<reduce(std::plus<int>(), {1, 2, 3, 4, 5, 6, 7})>(); // error

bol*_*lov 5

为什么C ++编译器可以将函数声明为constexpr,而不能将其声明为constexpr?

没有。但是您没有定义函数constexpr。您正在定义模板。

让我们设置:

struct Is_constexpr {
  constexpr Is_constexpr() = default;
  constexpr auto bar() {
    return 24;
  }
};

struct Not_constexpr {
  auto bar() {
    return 24;
  }
};
Run Code Online (Sandbox Code Playgroud)

现在,如果您尝试将使用Not_constexpr编译器的函数(而不是模板)定义为constexpr,则不会:

constexpr auto foo_function(Not_constexpr v)
{
  return v.bar();
  // error: call to non-constexpr function 'auto Not_constexpr::bar()'
}
Run Code Online (Sandbox Code Playgroud)

但是,您正在定义模板。让我们看看这是怎么回事:

template <class T>
constexpr auto foo(T v)
{
  return v.bar();
}
Run Code Online (Sandbox Code Playgroud)

编译器允许您执行此操作。没错 为什么?因为它是模板。某些实例化可能是constexpr,有些则不是,具体取决于T

int main() {
  constexpr Is_constexpr is_c;
  constexpr Not_constexpr not_c;

  std::integral_constant<int, foo(is_c)> a; // OK
  //std::integral_constant<int, foo(not_c)> a; // ERROR

}
Run Code Online (Sandbox Code Playgroud)


Whi*_*TiM 5

让我们直接从第4.1节第三段中的建议www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2235.pdf出发:我引用:

可以使用非常数表达式来调用常数表达式函数,在这种情况下,不需要在编译时评估结果值。

看到这个问题:constexpr函数何时在编译时求值?

template<typename Functor, typename T>
T constexpr reduce(Functor f, std::initializer_list<T> il) {
  return std::accumulate(std::next(il.begin()), il.end(), *(il.begin()), f);
}
Run Code Online (Sandbox Code Playgroud)

再次,正如您所知,std::accumulate这不是constexpr功能。

template<int value>
void print_constexpr() { std::cout << value << std::endl; }
Run Code Online (Sandbox Code Playgroud)

同样,如您所知,非类型模板参数必须是常量表达式


现在:

template<typename Functor, typename T>
T constexpr reduce(Functor f, std::initializer_list<T> il) {
  return std::accumulate(std::next(il.begin()), il.end(), *(il.begin()), f);
}
Run Code Online (Sandbox Code Playgroud)

关于它为什么起作用:这是C ++标准必须说的:

[dcl.constexpr / 6](重点是我):

如果constexpr函数模板或类模板的成员函数的实例化模板特化将无法满足对函数或constexpr构造函数要求constexpr,则该特化仍然是一个constexpr函数constexpr 构造函数,即使对该函数的调用不会出现在其中。一个常量表达式 ...

注:

从功能模板实例化的功能称为功能模板专门化;


如果不是模板,它将失败:

int constexpr reduce(int(*f)(int, int), std::initializer_list<int> il) {
  return std::accumulate(std::next(il.begin()), il.end(), *(il.begin()), f);
}
Run Code Online (Sandbox Code Playgroud)

现在,编译器会抱怨您无法constexpr在定义为constexpr