This is a similar question to Using std::forward on sub fields, but the answer there doesn't seem to apply in my case.
template<class Base, class F>
void visit(Base&&, const F&) {
throw std::bad_cast();
}
template<class Derived, class... Rest, class Base, class F>
void visit(Base&& base, const F& f) {
if (auto *as_derived = dynamic_cast<Derived *>(&base)) {
return f(std::forward<Base>(*as_derived));
} else {
return visit<Rest...>(std::forward<Base>(base), f);
}
}
My goal is that the following test case work:
struct Animal {
virtual ~Animal() {}
};
struct Cat : Animal {
void speak() & { puts("meow"); }
void yowl() && { puts("MEOW!"); }
};
struct Dog : Animal {
void speak() & { puts("woof"); }
void yowl() && { puts("WOOF!"); }
};
int main() {
Animal *a = new Cat();
Animal *b = new Dog();
visit<Cat, Dog>(*a, [](auto&& a){ std::forward<decltype(a)>(a).speak(); });
visit<Cat, Dog>(*b, [](auto&& a){ std::forward<decltype(a)>(a).speak(); });
visit<Cat, Dog>(std::move(*a), [](auto&& a){ std::forward<decltype(a)>(a).yowl(); });
visit<Cat, Dog>(std::move(*b), [](auto&& a){ std::forward<decltype(a)>(a).yowl(); });
}
Desired output: "meow" "woof" "MEOW!" "WOOF!". Notice that the functions speak
and yowl
are non-virtual; this is required in my original code because they are actually templates, and templates can't be virtual.
The problem with this code as written here is that std::forward<Base>(*as_derived)
doesn't merely change the ref-qualifiers and const-qualifiers on *as_derived
to enable perfect forwarding; it actually casts the type back up to Base&
, nerfing the whole point of visit
!
Is there a standard library function that does what I want std::forward
to do — namely, change the ref-qualifiers and const-qualifiers on *as_derived
to match the ones that would be perfect-forwardly deduced from std::forward<Base>
?
If there's no standard library function, how could I write a "perfect forward a child type" function for my own use?
The Wandbox link above contains something that "works" for this test case, but it doesn't preserve constness and it doesn't look elegant at all.