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?
I see two problems:
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);
return
s 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.