Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I define an out-of-line class template member function with a non-trailing decltype return type

template <class T>
struct foo {
    int x;
    decltype(x) f1();
};

It seems to be impossible to define f1 out-of-line. I have tried the following definitions, and none of them work:

template <class T> decltype(x) foo<T>::f1() {}
template <class T> auto foo<T>::f1() -> decltype(x) {}
template <class T> auto foo<T>::f1() { return x; }
template <class T> decltype(std::declval<foo<T>>().x) foo<T>::f1() {}
// This return type is copied from the gcc error message
template <class T> decltype (((foo<T>*)(void)0)->foo<T>::x) foo<T>::f1() {}

This isn't a problem in real code because changing the in-class declaration of f1 to auto f1() -> decltype(x); allows the second definition. but I'm puzzled as to why that changes anything. Is it even possible to declare the original f1 out-of-line?

like image 859
Marc Aldorasi Avatar asked Mar 19 '18 21:03

Marc Aldorasi


People also ask

How would you define member function outside the class template?

Member functions of class templates (C++ only) You may define a template member function outside of its class template definition. The overloaded addition operator has been defined outside of class X . The statement a + 'z' is equivalent to a. operator+('z') .

What is the return type of Decltype?

If the expression parameter is a call to a function or an overloaded operator function, decltype(expression) is the return type of the function. Parentheses around an overloaded operator are ignored. If the expression parameter is an rvalue, decltype(expression) is the type of expression .

Can a non template class have a template member function?

A non-template class can have template member functions, if required. Notice the syntax. Unlike a member function for a template class, a template member function is just like a free template function but scoped to its containing class.

Which is used to define the member function outside of the class * :: none?

To define a member function outside the class definition we have to use the scope resolution :: operator along with class name and function name.


1 Answers

As dumb as this might seem, I believe the following is correct:

template <class T>
struct foo {
    int x;
    decltype(x) f1();
};

template <class T>
int foo<T>::f1() { return 0; }

Clang accepts it, but GCC doesn't, so I am going to say that I think GCC has a bug. [Coliru link]

The issue is whether these two declarations of f1 declare the same function (more technically, the same member function of the same class template). This is governed by [basic.link]/9, according to which:

Two names that are the same (Clause 6) and that are declared in different scopes shall denote the same variable, function, type, template or namespace if

  • both names have external linkage or else both names have internal linkage and are declared in the same translation unit; and
  • both names refer to members of the same namespace or to members, not by inheritance, of the same class; and
  • when both names denote functions, the parameter-type-lists of the functions (11.3.5) are identical; and
  • when both names denote function templates, the signatures (17.5.6.1) are the same.

The requirements appear to be satisfied, provided that the return types are in fact the same (since the return type is part of the signature for a class member function template, according to [defns.signature.member.templ]). Since foo<T>::x is int, they are the same.

This would not be the case if the type of x were dependent. For example, GCC and Clang both reject the definition when the declaration of x is changed to typename identity<T>::type x;. [Coliru link] In that case, [temp.type]/2 would apply:

If an expression e is type-dependent (17.6.2.2), decltype(e) denotes a unique dependent type. Two such decltype-specifiers refer to the same type only if their expressions are equivalent (17.5.6.1). [ Note: However, such a type may be aliased, e.g., by a typedef-name. — end note ]

Perhaps GCC is in error for considering x to be type-dependent (it shouldn't be). However, this note suggests a workaround:

template <class T>
struct foo {
    int x;
    decltype(x) f1();
    using x_type = decltype(x);
};

template <class T>
typename foo<T>::x_type foo<T>::f1() { return 0; }

This works on both GCC and Clang. [Coliru link]

like image 194
Brian Bi Avatar answered Oct 25 '22 21:10

Brian Bi