Jon Watte
Jon Watte

Reputation: 7228

Why does GCC not find my non-template function? ("no matching function for call to...")

In MSVC 2008, I have the following code:

class Foo {
  //  Be a little smarter about deriving the vertex type, to save the user some typing.
  template<typename Vertex> inline void drawVertices(
    Elements vCount, RenPrim primitiveType, PixMaterial *mtl, Vertex const *vertices)
  {
    this->drawVertices(vCount, primitiveType, mtl, vertices, Vertex::VertexType);
  }

  virtual void drawVertices(
    Elements vCount,
    RenPrim primitiveType,
    PixMaterial *mtl,
    void const *vertices,
    uint vertexType) = 0;
};

I use it something like:

struct RenFlexibleVertexPc
{
  enum { VertexType = RenVbufVertexComponentsPc };
  float x;
  float y;
  float z;
  GraVideoRgba8 c; // Video format, not external!
};


  PixMaterial *material;
  struct Pc : RenFlexibleVertexPc
          {
            void set(Triple t, uint cl) { x = (float)t.x_; y = (float)t.y_; z = (float)t.z_; c = cl; }
          } vpc[4];
  ...
  Foo *renderer;
  renderer->drawVertices(4, RenPrimTriangleFan, material, vpc);

This works fine in MSVC 2008 SP1. However, GCC (3.4 and 4.1,2) throws a "no matching function for call to function" error, apparently not seeing the template when there is a non-template function with more arguments.

Is GCC broken, or is my code broken, and if so, why?

Upvotes: 0

Views: 1309

Answers (3)

Max Lybbert
Max Lybbert

Reputation: 20039

There is no problem with overloading or inheritance:

#include <iostream>
#include <memory>

namespace {
    struct A {
        virtual void f()
        {
            std::cout<<"inside A's f()\n";
        }

        template <typename T> void f(T t)
        {
            std::cout<<T::i<<'\t';
            this->f();
        }
    };

    struct B : A {
        void f()
        {
            std::cout<<"hello\t";
            A::f();
        }
    };

    struct C {
        static const unsigned int i = 5;
    };

    struct D {
        enum { i = 6 };
    };
}

int main()
{
    std::auto_ptr<A> b(new B());
    b->f(C());
    b->f(D());
}

Works correctly. On the other hand, the smallest example I can find that exhibits your problem does not have inheritance or overloading:

#include <iostream>

namespace {
    struct A {
        template<class C> void print(C c)
        {
            c.print();
        }
    };
}

int main()
{
    struct B {
        void print()
        {
            std::cout << "whee!\n";
        }
    };

    A a;
    B b;
    a.print(b);
}

Note that if struct B is defined in a namespace (whether it's an unnamed namespace, or a completely different namespace, or the global namespace) instead of inside main() that this compiles without error.

I don't know enough of the standard to say if this is a bug, but it appears to be one. I've gone ahead and reported it to the GCC bug database.


And here's your answer from the GCC developers (from the link above): "Local classes cannot be template arguments."

So the code is broken. Not that it's a bad idea. In fact, C++0x removes this restriction.

I noticed the line

Note that the code works in GCC if I explicitly cast vpc to (RenFlexibleVertexPc *)

And since RenFlexibleVertexPc is not a local class this makes sense. However Pc is a local class/struct, so it is not allowed.

Upvotes: 1

outis
outis

Reputation: 77420

@OP: Specifying the template parameter is a valid approach.

renderer->drawVertices<RenFlexibleVertexPc>(4, RenPrimTriangleFan, material, vpc);

With Pete's additions, you code also compiles on Apple's GCC 4.0.1, so I suspect there's something your posted code is missing that's causing the problem.

@Max: GCC's treatment of your source is standard. Struct B is local to main(), so B (and thus main()::B::print()) is not visible outside main(). As you're probably aware, moving the definition of B outside of main() and it will compile.

Upvotes: 1

dirkgently
dirkgently

Reputation: 111200

The definition of VertexType is already in the code (an enum). Elements is an unsigned long. Note that the code works in GCC if I explicitly cast vpc to (RenFlexibleVertexPc *)

If it's an enum why pass an object of type array 4 of struct? What is RenFlexibleVertexPc? The last argument to drawVertices should either be a constant pointer to a Vertex object or a const* to an object of a class derived from Vertex.

Foo *renderer;
renderer->drawVertices(4, RenPrimTriangleFan, material, vpc);

You are calling a function on an uninitialized pointer. I hope this is not the real code. \

Upvotes: 0

Related Questions