vendredi 1 janvier 2016

Differences in constexpr evaluation in different compiler versions

I have the following program:

constexpr int flag (int);

template<class Tag>
struct writer {
  friend constexpr int flag (Tag) {
    return 0;
  }
};

template<bool B, class Tag = int>
struct dependent_writer : writer<Tag> { };

template<
  bool B = noexcept (flag (0)),
  int    =   sizeof (dependent_writer<B>)
>
constexpr bool f () {
  return B;
}

int main () {
  constexpr bool a = f();
  constexpr bool b = f();

  static_assert( !a, "was not instantiated" );
  static_assert( !b, "was not instantiated" ); // here's the difference
}

Interestingly, this program compiles with gcc 5.2 and clang 3.6 in C++11, C++14 and C++17 mode. However, with gcc 4.7, 4.8 and 4.9 the following error occurs:

main.cpp: In function 'int main()':
main.cpp:26:7: error: static assertion failed: was not instantiated
       static_assert( !b, "was not instantiated" ); // here's the difference
       ^

It appears, that in the old compilers, the default template parameters of the template function f() are decided upon at the function call site. The first time f() is called, the template class writer<int> gets instantiated finally, which defines the function external function flag(). This changes the value of noexcept(flag(0)), because the compiler can now tell by inspection, that this function is noexcept, since it finds a definition, the second time f() is called. Therefore, the found template arguments are different the second time f() is called.

On the other hand, the newer compilers seem to decide on the default template parameters at the point the template function f() is declared and therefore the result is the same both times.

This former behaviour (with gcc 4.7 to 4.9) appears a bit strange to me. I would like to know, if that behaviour was standard conforming anyways and if not, which part of the standard it is contradicting.

NOTE: The question Compile time template instantiation check has an accepted answer that relies on the behaviour of the old compilers. So answering this question will help to find out, whether the accepted solution is correct.

Aucun commentaire:

Enregistrer un commentaire