Dependent type or argument in decltype in function definition fails to compile when declared without decltype

Viewed 1254

I've been playing with deduced return types in definitions that resolve to the same type as the declaration. This works:

template <typename>
struct Cls {
  static std::size_t f();
};

template <typename T>
decltype(sizeof(int)) Cls<T>::f()  { return 0; }

But if I change the definition to something that should be equivalent by replacing sizeof(int) with sizeof(T) it fails

template <typename T>
decltype(sizeof(T)) Cls<T>::f() { return 0; }

gcc's error (clang is almost identical):

error: prototype for ‘decltype (sizeof (T)) Cls<T>::f()’ does not match any in class ‘Cls<T>’
 decltype(sizeof(T)) Cls<T>::f() { return 0; }
                     ^~~~~~
so.cpp:4:24: error: candidate is: static std::size_t Cls<T>::f()
     static std::size_t f();
                        ^

The same problem arises with function parameter types:

template <typename>
struct Cls {
  static void f(std::size_t);
};

template <typename T>
void Cls<T>::f(decltype(sizeof(T))) { } // sizeof(int) works instead

Stranger yet, if the declaration and definition match and both use decltype(sizeof(T)) it compiles successfully, and I can static_assert that the return type is size_t. The following compiles successfully:

#include <type_traits>

template <typename T>
struct Cls {
  static decltype(sizeof(T)) f();
};

template <typename T>
decltype(sizeof(T)) Cls<T>::f() { return 0; }

static_assert(std::is_same<std::size_t, decltype(Cls<int>::f())>{}, "");

Update with another example. This isn't a dependent type but still fails.

template <int I>
struct Cls {
  static int f();
};

template <int I>
decltype(I) Cls<I>::f() { return I; }

If I use decltype(I) in both the definition and declaration it works, if I use int in both the definition and declaration it works, but having the two differ fails.


Update 2: A similar example. If Cls is changed to not be a class template, it compiles successfully.

template <typename>
struct Cls {
  static int f();
  using Integer = decltype(Cls::f());
};

template <typename T>
typename Cls<T>::Integer Cls<T>::f() { return I; }

Update 3: Another failing example from M.M. A templated member function of a non-templated class.

struct S {
  template <int N>
  int f();
};

template <int N>
decltype(N) S::f() {}

Why is it illegal for the declaration and definition to disagree with a dependent type only? Why is it affected even when the type itself isn't dependent as with the template <int I> above?

1 Answers
Related