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?
std::plus
- constexpr T operator()( const T& lhs, const T& rhs ) const;
(since C++14) - constexpr: http://en.cppreference.com/w/cpp/utility/functional/plus
constexpr initializer_list();
(since C++14) - construcot of initializer_list
is constexpr: http://en.cppreference.com/w/cpp/utility/initializer_list/initializer_list
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
:
-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
For all these cases compile OK, until unused line: //print_constexpr<reduce(std::plus<int>(), {1, 2, 3, 4, 5, 6, 7})>(); // error
Why does the C++ compiler makes it possible to declare a function as constexpr, which can not be constexpr?
It doesn't. But you are not defining a function constexpr
. You are defining a template.
Let's set up:
struct Is_constexpr {
constexpr Is_constexpr() = default;
constexpr auto bar() {
return 24;
}
};
struct Not_constexpr {
auto bar() {
return 24;
}
};
Now if you try to define a function (not a template) as constexpr that uses Not_constexpr
the compiler won't let you:
constexpr auto foo_function(Not_constexpr v)
{
return v.bar();
// error: call to non-constexpr function 'auto Not_constexpr::bar()'
}
You are however defining a template. Let's see how this goes:
template <class T>
constexpr auto foo(T v)
{
return v.bar();
}
The compiler lets you do this. No error. Why? Because it's a template. Some instantiations may be constexpr
, some not, depending on 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
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With