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/AGwniRNRb
If you do write this code:
constexpr int result = reduce(std::plus<int>(), {1, 2, 3, 4, 5, 6, 7});
you will see that reduce doesn't not produce a constexpr result.
The reason is because "note: non-constexpr function 'accumulate >' cannot be used in a constant expression" And as you can see here - http://en.cppreference.com/w/cpp/algorithm/accumulate
std::accumulate is not constexpr
EDIT extending to answer the actual question, thanks @peterchen: It's compiled when it hits the usage, it doesn't and couldn't try and resolve the function until it compiles the specific version of the template. When it hits the usage and triggers the compile, it resolves the accumulate and sees it's not constexpr, so issues an 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 orconstexpr
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
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
}