Search code examples
c++templatesdependent-name

Compiler confuses name of an (unrelated) template with method name


I have a compile error in the following code. It seems that the compiler interprets class method set as a template which - at first glance - is completely unrelated to my code.

#include <cassert>
#include <limits>

using namespace std;

template <class T>
class ReduceScalar{

    public:
        T get() { return *r; };
        void set(T t) { *r = t; };
        void set(T* t) { r = t; };

    private:
        T* r;

};

template <class T>
class ReduceSum : public ReduceScalar<T>
{
    public:
        ReduceSum(T* target) { set(target); set(0); } // COMPILE ERROR


};

Compiler gives the following error:

../test/../io/scalarreducers.h:34:26: error: use of class template 'set' requires template arguments
                ReduceSum(T* target) { set(target); set(0); }

But I think it's because it thinks that set is a template:

/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/__tree:685:71: note: template is declared here
        template <class, class, class> friend class _LIBCPP_TYPE_VIS_ONLY set;

I do not understand why the compiler tries to instantiate that template for method set and not just call method set. How can I resolve this name confusion?


Solution

  • You will still have problems even if you get rid of that nasty using namespace std. The problem is that member function set may not exist in all instantiations. The code in the question uses set as an unqualified, non-dependent name. This means two things:

    • The compiler will try to resolve set at the point where the template is defined.
    • The compiler will not look into the base class ReduceScalar<T> for member function set. It can't because that member might not exist for all instantiations.

    End result: The code doesn't compile. The solution is to turn that non-dependent name into a dependent name. This defers resolution of dependent names until the template instantiation. One way to do this is to explicitly use this (which is a dependent name).

    template <class T>
    class ReduceSum : public ReduceScalar<T>
    {
    public:
        ReduceSum(T* target) { this->set(target); }
    };
    

    Alternatively, you can use the using declaration (very different from the using directive):

    template <class T>
    class ReduceSum : public ReduceScalar<T>
    {
    public:
        using ReduceScalar<T>::set;
        ReduceSum(T* target) { set(target); }
    };