Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Template member functions with trailing return type, giving errors even if unused

I understand that template member functions are only generated if used. This is convenient if not all used types support such a function. However, this does not appear to work for functions with trailing return type specification. Below is a small experiment:

// helper function for case A workaround
template <typename A, typename T>
auto F(T&& x)
-> decltype(x.template f <A>())
    { return x.template f <A>(); }

// helper function for case B workaround
template <typename A, typename T>
auto G(T&& x)
-> decltype(x.g())
    { return x.g(); }

template <typename T>
struct S
{
    // case A: not ok in GCC + Clang
    template <typename A>
    auto f1()
    -> decltype(T().template f <A>())
        { return T().template f <A>(); }

    // case A workaround: ok in Clang + GCC
    template <typename A>
    auto f2()
    -> decltype(F <A>(T()))
        { return F <A>(T()); }

    // case B: ok in GCC, not ok in Clang
    template <typename A>
    auto g1()
    -> decltype(T().g())
        { return T().g(); }

    // case B workaround: ok in GCC + Clang
    template <typename A>
    auto g2()
    -> decltype(G <A>(T()))
        { return G <A>(T()); }
};

Please keep in mind that this sample is only meant to illustrate the issue, it is not useful in anything else.

S <T> can be instantiated for any type T that has appropriate member functions f, g.

However, if I try to instantiate S <int>, e.g. by S <int> s{};, I do get errors like type 'int' is not a structure or union. This happens for both cases f1, g1, which attempt to call template function f or non-template function g respectively on a value of type T (int in this case). It happens even though I am not trying to call f1 or g1 on object s. However, GCC is fine with the case of g1; Clang is not.

A workaround for case A (template member function f) is to use a helper function F, which is what f2 does, and works fine for both Clang and GCC. It appears to work because call T().template f <A>() is hidden from the declaration of f2, and the compiler is not looking into F <A>(T()) when type A is unknown.

The same workaround for case B (non-template member function g) is also working for both compilers.

I would appreciate some help in finding out what is going on. Which is the correct behaviour in each case? Which compiler is correct? Is there any other workaround in general?

I am using GCC 4.8.1 and Clang 3.3.

like image 614
iavr Avatar asked Jan 20 '14 03:01

iavr


People also ask

Why use trailing return type c++?

The trailing return type feature removes a C++ limitation where the return type of a function template cannot be generalized if the return type depends on the types of the function arguments.

Can return type be auto?

In C++14, you can just use auto as a return type.

Which member function does not require return type?

Which member function doesn't require any return type? Explanation: All the member functions work same as normal functions with syntax. But the constructor and destructor are also considered as member functions of a class, and they never have any data type.

How to instantiate a template function in c++?

To instantiate a template function explicitly, follow the template keyword by a declaration (not definition) for the function, with the function identifier followed by the template arguments. template float twice<float>(float original); Template arguments may be omitted when the compiler can infer them.


Video Answer


1 Answers

SFINAE only applies to the template arguments of the function, not the one inherited from the class.

A different solution is to include copy T to a second template argument but this is nothing more than a shorter version of your workaround:

#include <utility>
#include <type_traits>    
struct Foo {
    template < typename T > T f() { return {}; }
};
template <typename T>
struct S {
    template <typename A, typename TT = T >
    auto f1()  -> decltype(std::declval<TT>().template f <A>()) { 
        static_assert(std::is_same<T,TT>::value, "TT must be equal to T" );
        return TT().template f <A>(); 
    }
};

int main() {
    S<Foo> a;
    a.f1<int>(); // ok

    S<int> b;
    b.f1<int>(); // not ok
}
like image 189
galop1n Avatar answered Sep 27 '22 22:09

galop1n