Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does the C++ compiler makes it possible to declare a function as constexpr, which can not be 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?

  • 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:

  • x86 GCC 7.0.0 -std=c++1z -O3: http://melpon.org/wandbox/permlink/AGwniRNRbfmXfj8r
  • x86 gcc 4.9.2 -std=c++14 -O3: https://godbolt.org/g/wmAaDT
  • x86 gcc 6.1 -std=c++14 -O3: https://godbolt.org/g/WjJQE5
  • x86 clang 3.5 -std=c++14 -O3: https://godbolt.org/g/DSCpYv
  • x86 clang 3.8 -std=c++14 -O3: https://godbolt.org/g/orSrgH
  • x86 Visual C++ - you should copy-paste code to: http://webcompiler.cloudapp.net/
  • ARM gcc 4.8.2, ARM64 gcc 4.8, PowerPC gcc 4.8, AVR gcc 4.5.3 - doesn't support 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

like image 665
Alex Avatar asked Dec 05 '22 17:12

Alex


1 Answers

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

}
like image 173
bolov Avatar answered Feb 16 '23 03:02

bolov