Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Error in template instantiation in GCC 4.9, works fine in GCC 4.8

The test code below works perfectly fine with GCC 4.8 (and 4.7):

#include <type_traits>

template<typename T, T &object, typename... Args>
struct Functor
{
    template<float (T::*function)(Args...), Args... args>
    struct Inner
    {
        float operator()() const
        {
            return (object.*function)(args...);
        }
    };
};

class Object
{
public:

    float someFunction()
    {
        return {};
    }

    float someFunctionWithArgument(int)
    {
        return {};
    }
};

Object object;

Functor<Object, object>::template Inner<&Object::someFunction> functor1;
Functor<Object, object, int>::template Inner<&Object::someFunctionWithArgument, 1> functor2;

int main()
{

}

However with GCC 4.9 it fails with a rather unhelpful message at the point of instantiation of functor1:

$ g++ -std=c++11 test.cpp 
test.cpp: In instantiation of ‘struct Functor<Object, (* & object)>’:
test.cpp:33:24:   required from here
test.cpp:7:9: error: wrong number of template arguments (2, should be 1)
  struct Inner
         ^
test.cpp:7:9: error: provided for ‘template<class T, T& object, class ... Args> template<float (T::* function)(Args ...), Args ...args> struct Functor<T, object, Args>::Inner’
test.cpp:7:9: error: wrong number of template arguments (2, should be 1)
test.cpp:7:9: error: provided for ‘template<class T, T& object, class ... Args> template<float (T::* function)(Args ...), Args ...args> struct Functor<T, object, Args>::Inner’
test.cpp:33:35: error: ‘Inner’ in ‘struct Functor<Object, (* & object)>’ does not name a template type
 Functor<Object, object>::template Inner<&Object::someFunction> functor1;

If I comment the line with functor1 instantiation, everything else (functor2) works fine.

Any ideas how to solve that?

EDIT:

I reported a bug in GCC - https://gcc.gnu.org/bugzilla/show_bug.cgi?id=64514 we'll see...

like image 583
Freddie Chopin Avatar asked Jan 06 '15 16:01

Freddie Chopin


1 Answers

I am not a hundert percent sure about whether GCC is entirely wrong. However, the problem case can essentially be reduced to

template<typename... T>
struct Functor
{
    template <T...>
    struct Inner
    {};
};

template struct Functor<>::Inner<>;

Which shows the same behavior with GCC. This code seems to be well-formed - even though Inner doesn't have any template arguments:

When N is zero, the instantiation of the expansion produces an empty list. Such an instantiation does not alter the syntactic interpretation of the enclosing construct, even in cases where omitting the list entirely would otherwise be ill-formed or would result in an ambiguity in the grammar.

But if we now change the code to use an alias template instead, it suddenly works:

template <typename... T>
struct Functor
{
    template <T...>
    using Inner = void;
};

using type = Functor<>::Inner<>;

Demo. While trying to apply this solution to your problem, not only did I encounter the original bug but also a second one:

template <typename... Args>
struct Functor
{
    template <Args... args>
    struct A;

    template <Args... args>
    using B = A<args...>;
};

using type = Functor<>::B<>;

main.cpp:8:24: error: expansion pattern 'args' contains no argument packs

 using B = A<args...>
                    ^

I think that GCC has a fundamental problem with "empty" non-type template parameters.

like image 70
Columbo Avatar answered Oct 13 '22 22:10

Columbo