Search code examples
c++templatesreturn-typetemplate-argument-deduction

No instance of function template matches the argument list, argument types are: (std::string, CommandLineArgumentTypes)


I have a function template that takes a std::string and an enum value that describes the type of data contained within the string. It converts the string into and returns a std::string, int, unsigned int, or bool depending on the enum value.

template <typename T> T parseInput(std::string &input, CommandLineArgumentTypes &type) {
    switch (type) {
        case CommandLineArgumentTypes::String :
            return input;
        case CommandLineArgumentTypes::Int :
            if (int value = std::stoi(input)) {
                return value;
            }

            if (input.size() > 1) {
                if (input[0] == "0" && input[1] == "x") {
                    if (int value = std::stoi(input.substr(1, input.size() - 2))) {
                        return value;
                    }
                }
            }

            return NULL;
        case CommandLineArgumentTypes::UInt :
            return (unsigned int)std::stoi(input);
        case CommandLineArgumentTypes::Flag :
            return true;
    }
}

When I make a call to the function template

parseInput(arg, type);

where arg is a string and type is CommandLineArgumentTypes, I get the error

no instance of function template matches the argument list, argument types are: (std::string, CommandLineArgumentTypes).

How can I get the template to determine the return type, why does this error occur when the arguments do match the argument list, and what is a better way to do this?


Solution

  • I see two problems:

    1. the template argument T isn't deducible from the type of the arguments (input and type).

    I understand that your intention is deduce it from the value of the type, but this, simply, doesn't works in C++.

    You have to explicit it, calling the function; by example

     parseInput<bool>(arg, type);
    
    1. your function has different returns with different types. And they are incompatibles.

    Before C++17 (before if constexpr) this can't be done.

    In C++17, it can be done, (with if constexpr, not with switch) but only if the test is based on a compile-time known value.

    So, if you pass type as template parameter (if you know its value compile-time, obviously) you can write something as (caution: code not tested)

    template <CommandLineArgumentTypes type>
    auto parseInput (std::string &input)
     {
       if constexpr ( CommandLineArgumentTypes::String == type )
           return input;
       else if constexpr ( CommandLineArgumentTypes::Int == type )
        {
          // do something else
        }
       else if constexpr ( CommandLineArgumentTypes::UInt == type )
          return (unsigned int)std::stoi(input);
       else if constexpr ( CommandLineArgumentTypes::Flag == type )
          return true;
       // else ?
    }
    

    The call become, by example,

     parseInput<CommandLineArgumentTypes::UInt>(arg);
    

    but, I repeat, this can works only if the template argument (the old type) is known compile type.