为什么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;
}
输出:
28
28
6
5
2
为什么错误在这一行://print_constexpr<reduce(std::plus<int>(), {1, 2, 3, 4, 5, 6, 7})>(); // error
即使对于C ++ 14和C ++ 1z ?
std::plus
- constexpr T operator()( const T& lhs, const T& rhs ) const;
(自C ++ 14起) - constexpr :http://en.cppreference.com/w/cpp/utility/functional/plus constexpr initializer_list();
(自C ++ 14开始) - initializer_list
的构造函数 constexpr :http://en.cppreference.com/w/cpp/utility/initializer_list/initializer_list 为什么编译器允许将reduce()
标记为constexpr
,但reduce()
无法用作模板参数,即使传递给reduce()
的所有参数都已知编译时?
对某些编译器也有同样的效果 - 支持C ++ 14 -std=c++14
:
-std=c++1z -O3
:http://melpon.org/wandbox/permlink/AGwniRNRbfmXfj8r -std=c++14 -O3
:https://godbolt.org/g/wmAaDT -std=c++14 -O3
:https://godbolt.org/g/WjJQE5 -std=c++14 -O3
:https://godbolt.org/g/DSCpYv -std=c++14 -O3
:https://godbolt.org/g/orSrgH -std=c++14
对于所有这些情况,编译确定,直到未使用的行://print_constexpr<reduce(std::plus<int>(), {1, 2, 3, 4, 5, 6, 7})>(); // error
答案 0 :(得分:4)
让我们直截了当地提出建议, www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2235.pdf在第4.1节第3段中:我引用:
可以使用非常量调用常量表达式函数 表达式,在这种情况下,没有要求得到的结果 值在编译时进行评估。
请参阅此问题: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);
}
同样,正如您所知,std::accumulate
不是constexpr
函数。
template<int value>
void print_constexpr() { std::cout << value << std::endl; }
如你所知,再次non-type template arguments must be constant expressions。
立即强>
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 ++标准所说的:
[dcl.constexpr/6](强调我的):
如果实例化了constexpr函数的模板特化 类模板的模板或成员函数将无法满足 requirements for a
constexpr
function或constexpr构造函数, 该专业化仍然是constexpr
函数或constexpr
构造函数,即使调用此类函数也无法出现 一个恒定的表达 ......
注意:that
从函数模板实例化的函数称为函数 模板专业化;
当它不是模板时,它将失败:
int constexpr reduce(int(*f)(int, int), std::initializer_list<int> il) {
return std::accumulate(std::next(il.begin()), il.end(), *(il.begin()), f);
}
编译器现在会抱怨您无法在定义为constexpr
的函数中调用非constexpr
函数
答案 1 :(得分:3)
如果您确实写了这段代码:
constexpr int result = reduce(std::plus<int>(), {1, 2, 3, 4, 5, 6, 7});
你会发现reduce不会产生constexpr结果。
原因是因为 &#34;注意:非constexpr功能&#39;累积&gt;&#39;不能用于常数表达式&#34; 正如你在这里看到的那样 - http://en.cppreference.com/w/cpp/algorithm/accumulate
std :: accumulate不是constexpr
EDIT延伸回答实际问题,谢谢@peterchen: 它在达到使用时进行编译,在编译模板的特定版本之前,它不会尝试解析该函数。当它达到使用并触发编译时,它会解析累积并看到它不是constexpr,因此发出错误。
答案 2 :(得分:3)
为什么C ++编译器可以将函数声明为constexpr,而不能是constexpr?
没有。但是你没有定义一个函数constexpr
。您正在定义模板。
让我们设置:
struct Is_constexpr {
constexpr Is_constexpr() = default;
constexpr auto bar() {
return 24;
}
};
struct Not_constexpr {
auto bar() {
return 24;
}
};
现在,如果您尝试将函数(不是模板)定义为使用Not_constexpr
的constexpr,编译器将不允许您:
constexpr auto foo_function(Not_constexpr v)
{
return v.bar();
// error: call to non-constexpr function 'auto Not_constexpr::bar()'
}
然而,您正在定义模板。让我们看看这是怎么回事:
template <class T>
constexpr auto foo(T v)
{
return v.bar();
}
编译器允许您执行此操作。没错。为什么?因为它是一个模板。某些实例化可能是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
}