为什么C ++编译器可以声明一个函数为constexpr,不能是constexpr? [英] Why does the C++ compiler makes it possible to declare a function as constexpr, which can not be constexpr?

查看:246
本文介绍了为什么C ++编译器可以声明一个函数为constexpr,不能是constexpr?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

为什么C ++编译器可以将一个函数声明为constexpr,它不能是constexpr?



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

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

template< 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函子,类型名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函子,类型名T,类型名... 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<值<< 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;
}

输出:

  28 
28
6
5
2

为什么在这行出错: // 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





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

解决方案

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


可以使用非常量
表达式调用常量表达式函数,在这种情况下,不需要在编译时对生成的
值进行求值。


查看此问题:一个constexpr函数何时在编译时被求值?

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

同样,如你所知, std :: accumulate 不是 constexpr 函数。

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

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






现在:

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

至于为什么它工作:这里是C ++标准说: / p>

[dcl.constexpr / 6 ] (强调我的):


如果实例化模板特化一个constexpr函数
模板或成员函数类模板将无法满足
constexpr function 或constexpr构造函数
专业化仍然是 constexpr 函数 constexpr
构造函数,即使调用这样的函数不能出现在
a常量表达式
...


注意:


从函数模板实例化的函数称为函数
模板专门化; / p>






当它不是模板时,它会失败:

  int constexpr reduce(int(* f)(int,int),std :: initializer_list< int& i){
return std :: accumulate(std :: next(il.begin()),il.end(),*(il.begin()),f);
}

编译器现在会抱怨你不能调用非< c> constexpr 函数定义为 constexpr


Why does the C++ compiler makes it possible to declare a function as constexpr, which can not be constexpr?

For example: 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;
}

Output:

28
28
6
5
2

Why error at this line: //print_constexpr<reduce(std::plus<int>(), {1, 2, 3, 4, 5, 6, 7})>(); // error even for C++14 and C++1z?

Why does compiler allow to mark reduce() as constexpr, but reduce() can't be used as template parameter even if all parameters passed to reduce() known at compile-time?


The same effect for some compilers - which supported C++14 -std=c++14:

For all these cases compile OK, until unused line: //print_constexpr<reduce(std::plus<int>(), {1, 2, 3, 4, 5, 6, 7})>(); // error

解决方案

Let's go straight from it's proposal, www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2235.pdf in section 4.1, third paragraph: and I quote:

A constant-expression function may be called with non-constant expressions, in that case there is no requirement that the resulting value be evaluated at compile time.

See this question: When does a constexpr function get evaluated at compile time?

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

Again, as you know, std::accumulate isn't a constexpr function.

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

Again, as you know, non-type template arguments must be constant expressions.


Now:

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

As to why it works: Here's what the C++ standard has to say:

[dcl.constexpr/6] (emphasis mine):

If the instantiated template specialization of a constexpr function template or member function of a class template would fail to satisfy the requirements for a constexpr function or constexpr constructor, that specialization is still a constexpr function or constexpr constructor, even though a call to such a function cannot appear in a constant expression ...

Note: that

A function instantiated from a function template is called a function template specialization;


When its not a template, it will fail:

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

The compiler will complain now that you cannot call a non-constexpr function in a function defined as constexpr

这篇关于为什么C ++编译器可以声明一个函数为constexpr,不能是constexpr?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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