Search code examples
c++11templatesoverload-resolution

Template function overload resolution of T vs. SomeClass<T>


I want to understand why the void handleParam(const Str& name, Table<T> value) template overload never gets instantiated (note that the code below is illustrative). I'm sure there is a perfectly good reason why it doesn't work, I just want to understand that reason. I'm aware of (and using) the workaround using an intermediate templated struct. But if something like the below was possible it would simplify my code a lot.

class TypeHandler
{
public:
    TypeHandler(OtherClass& bi) : _bi(bi) { }

    template <typename T> void handleParam(const Str& name, Table<T> value)
    {
        // Never gets instantiated.
    }

    template <typename T> void handleParam(const Str& name, T value)
    {

    }
}

template<typename HandlerType>
void HandleParamValue::handleTab(DataBlock& data, HandlerType& handler) {

    ...

    // Table of floats.
    Table<float> tab;
    handler.handleParam<Table<float>>(param_name, tab);

    // etc.
    ...
}

template<typename HandlerType>
void ParamStore::Iterate(HandlerType& handler) {
    for (...) {

        ...

        if (is_table(type)) {
            HandleParamValue::handleTab<HandlerType>(_datablock, handler);
        }
        else {
            HandleParamValue::handle<HandlerType>(_datablock, handler);
        }
    }
}

// Kick the whole thing off.
TypeHandler handler(_some_instance);
_param_store->Iterate(handler);

Solution

  • The reason seems to be very simple: if you write

    handler.handleParam<Table<float>>(param_name, tab);
    

    you explicitly specify T = Table<float>. So handleParam(const Str& name, T value) is used. If you want to use handleParam(const Str& name, Table<T> value), you need to specify T = float:

    handler.handleParam<float>(param_name, tab);
    

    Or even

    handler.handleParam(param_name, tab);
    

    Because the compiler will select the most specialized version automatically.

    I used the following code for testing:

    #include <iostream>
    #include <vector>
    
    class TypeHandler
    {
    public:
    
        template <typename T> void handleParam(const std::string& name, std::vector<T> value)
        {
            std::cout << "std::vector<T>\n";
        }
    
        template <typename T> void handleParam(const std::string& name, T value)
        {
            std::cout << "T\n";
        }
    };
    
    template<typename HandlerType>
    void handleTab(const std::string& name, HandlerType& handler) {
        std::vector<float> tab;
        handler.handleParam(name, tab);
    }
    
    int main()
    {
        TypeHandler t;
        handleTab("dfds", t);
        return 0;
    }
    

    If it doesn't reflect your problem adequately, please explain it in a comment.