I initially had a working code. Within this class I had code something like this:
auto event =
std::dynamic_pointer_cast<EventA>(event);
Now, to avoid a lot of duplicate code, I extended the class with templates. In new implementation I want to do something like this:
auto event_A_B = [&]() { //Event can be type EventA or type EventB
if (std::is_same<T, A>::value) {
return std::dynamic_pointer_cast<EventA>(event);
}
else if (std::is_same<T, B>::value) {
return std::dynamic_pointer_cast<EventB>(event);
}
};
Even though EventA and EventB types are derived from same base class, shared_ptr (i.e event_A_B) fails to compile. It says, cannot convert from shared_ptr to shared_ptr.
What else I tried? I thought of using decltype to deduce return type but I was not sure of syntax and below code dint work either!!
auto event_A_B = [&](decltype(auto)) //Or some expression should go here -- not sure
{
if (std::is_same<T, A>::value) {
return std::dynamic_pointer_cast<EventA>(event);
}
else if (std::is_same<T, B>::value) {
return std::dynamic_pointer_cast<EventB>(event);
}
};
Can someone please help here?
CodePudding user response:
The problem is that you are using a normal if statement which will execute at runtime, because of that the different paths in your lambda have different return types. First you are returning std::shared_ptr<EventA> then std::shared_ptr<EventB>. It will then say that the EventB ptr is not the same type as the EventA return type you specified earlier. This is because the lambda return type is deduced by the first return statement that is detected. You could either define the return type of the lambda by specifying the base class shared_ptr as the return type manually, like:
auto event_A_B = [&]() -> std::shared_ptr<EventBase> {
if (std::is_same<T, A>::value) {
return std::dynamic_pointer_cast<EventA>(event);
}
else if (std::is_same<T, B>::value) {
return std::dynamic_pointer_cast<EventB>(event);
}
};
Or you could use if consetxpr in which case the different paths can have different return types, because this will make sure that only one of the return statements will actually exist. The if constexpr will actually generate a lambda with a different return type for each of your template class instances, which cannot change at runtime (hence the required constexpr).
auto event_A_B = [&] {
if constexpr (std::is_same_v<T, A>::value) {
return std::dynamic_pointer_cast<EventA>(event);
}
else if constexpr (std::is_same<T, B>::value) {
return std::dynamic_pointer_cast<EventB>(event);
}
};
CodePudding user response:
You could make a type trait to get rid of the if-branching.
template <typename T>
using PointerType = std::conditional_t<std::is_same<T, int>::value, int, double>;
template <typename T>
auto pointer_cast(T ptr) {
return std::dynamic_pointer_cast<PointerType<T>>(ptr);
}
If the condition to apply is more complex in your real use-case, then using a template struct and SFINAE or partial specialization could be more suited. The same principle can still be used.
