Reputation: 461
I got a function that needs to be able to accept different types, and then store them in seperate maps. (So if it receives an integer, it should go into the integer map, etc).
My current code:
template<typename T>
void set(const char* _key, T _value) {
DateTime* dt = new DateTime();
if(std::is_same<T, int>::value) {
settingsInt.insert(std::pair<const char*, int>(_key, _value));
printf("[%s][MESSAGE] Added integer setting (%s, %i)\n", dt->getDateTimeStamp(), _key, _value);
} else if(std::is_same<T, const char*>::value) {
printf("[%s][MESSAGE] Added string setting (%s, %s)\n", dt->getDateTimeStamp(), _key, _value);
} else if(std::is_same<T, double>::value) {
printf("[%s][MESSAGE] Added double setting (%s, %f)\n", dt->getDateTimeStamp(), _key, _value);
} else {
printf("[%s][WARNING] Trying to set setting of unknown type! (%s)\n", dt->getDateTimeStamp(), _key);
}
}
The printf functions display correctly, and the if statements correctly see which type is passed. However, when I try to insert the value into my map I get the following error:
error: invalid conversion from ‘const char*’ to ‘int’ [-fpermissive]
Is what I want possible with my approach? Is there a way to easily convert the template value back to its integer value?
Upvotes: 1
Views: 531
Reputation: 16156
Say you have code like
template<typename T>
void set(T _value) {
if(std::is_same<T, int>::value) {
_value + 42;
}
}
and you call that, say, with a struct Thing {}
. The compiler will check the types of all statements and expressions in the code at compile time (obviously, C++ is statically typed), and after instantiating the above template it'll check something similar to this:
void set(Thing _value) {
if (false) {
_value + 42;
}
}
The compiler doesn't care that the if
branch isn't taken, it sees the expression _value + 42
, and thus tries to find an operator+(Thing, int)
. Since we didn't define that, and there's no other viable function to call, we'd get a compiler message complaining about wrong operand types.
Getting back to your issue, what you want is different behaviour depending on some static (compile time / type level) condition. Unfortunately, there's no such thing as a "static if", which would be needed therefore, in the language. But of course there are ways to build one. Some examples:
void handle(int);
void handle(Thing);
template<typename T>
void set(T _value) {
handle(_value); // Or directly overload set
}
Same works using template specialisations.
std::conditional
struct IntHandler {
static void handle(int);
};
struct NoHandler {
template<typename T>
static void handle(T) {}
};
//
using handler = typename std::conditional<
std::is_same<T, int>::value, IntHandler, NoHandler>::type;
handler::handle(_value);
Considering you want maps for each type, I think something like this would suit your case:
template<typename...>
struct Map {};
template<typename First, typename... Rest>
struct Map<First, Rest...> : public Map<Rest...> {
std::map<char const *, First> _map;
void set(char const * key, First value) { _map[key] = value; }
};
Upvotes: 0
Reputation: 119877
You essentially say:
if (type of x is int)
insert x to the int map
if (type of x is char*)
insert x to the char* map
But this cannot work. The compiler wants to check that the insert statements are type-correct. The two insert statements
insert x to the int map
insert x to the char* map
cannot be type-correct at the same time. It is irrelevant whether the condition that guards each statement is true or false. A statement may never be executed but it must be type-correct regardless.
The correct way is to overload the set
function for each allowed type. No need to write a template. No need to handle the default case either, an eroneous call to set
simply will not compile.
Upvotes: 1