Sil*_*ann 5 c++ templates c++17
我想在不实现类的情况下使用lambdas实现多态访问者.我已经有了一个基础,但我正在为我的lambdas参数的类型演绎而苦苦挣扎.
假设我有一些遗留代码库,决定使用类型标签作为多态类型,如下所示:
enum class ClassType
{
BaseType = 0, TypeA, TypeB
};
class BaseType
{
public:
virtual ~BaseType() {}
ClassType getType() const
{ return type; }
protected:
ClassType type;
};
class TypeA : public BaseType
{
public:
static const ClassType Type = ClassType::TypeA;
explicit TypeA(int val) : val(val)
{ type = ClassType::TypeA; }
virtual ~TypeA() {}
int val;
};
class TypeB : public BaseType
{
public:
static const ClassType Type = ClassType::TypeB;
explicit TypeB(std::string s) : s(s)
{ type = ClassType::TypeB; }
virtual ~TypeB() {}
std::string s;
};
Run Code Online (Sandbox Code Playgroud)
我想要实现的是一个类似于std::variant访问者的访问者,如下所示:
std::vector<BaseType*> elements;
elements.emplace_back(new TypeA(1));
elements.emplace_back(new TypeB("hello"));
for (auto elem : elements)
{
visit(elem,
[](TypeA* typeA) {
std::cout << "Found TypeA element, val=" << typeA->val << std::endl;
},
[](TypeB* typeB) {
std::cout << "Found TypeB element, s=" << typeB->s << std::endl;
}
);
}
Run Code Online (Sandbox Code Playgroud)
我迄今为止实现这样一个visit<>()函数的失败方法是以下代码:
template <typename T>
struct identity
{
typedef T type;
};
template <typename T>
void apply_(BaseType* b, typename identity<std::function<void(T*)>&>::type visitor)
{
if (b->getType() != T::Type)
return;
T* t = dynamic_cast<T*>(b);
if (t) visitor(t);
}
template <typename... Ts>
void visit(BaseType* b, Ts... visitors) {
std::initializer_list<int>{ (apply_(b, visitors), 0)... };
}
Run Code Online (Sandbox Code Playgroud)
编译器抱怨它无法推断出T我的apply_函数的模板参数.
如何声明apply_正确捕获lambdas甚至其他callables 的正确模板和函数签名?或者这样的事情甚至可能吗?
这是一个(不完整的)解决方案,适用于任何具有一元、非重载、非模板化 operator()的函数对象。首先,让我们创建一个辅助类型别名来检索第一个参数的类型:
template <typename>
struct deduce_arg_type;
template <typename Return, typename X, typename T>
struct deduce_arg_type<Return(X::*)(T) const>
{
using type = T;
};
template <typename F>
using arg_type = typename deduce_arg_type<decltype(&F::operator())>::type;
Run Code Online (Sandbox Code Playgroud)
然后,我们可以在可变参数模板中使用折叠表达式来调用成功的任何函数对象:dynamic_cast
template <typename Base, typename... Fs>
void visit(Base* ptr, Fs&&... fs)
{
const auto attempt = [&](auto&& f)
{
using f_type = std::decay_t<decltype(f)>;
using p_type = arg_type<f_type>;
if(auto cp = dynamic_cast<p_type>(ptr); cp != nullptr)
{
std::forward<decltype(f)>(f)(cp);
}
};
(attempt(std::forward<Fs>(fs)), ...);
}
Run Code Online (Sandbox Code Playgroud)
使用示例:
int main()
{
std::vector<std::unique_ptr<Base>> v;
v.emplace_back(std::make_unique<A>());
v.emplace_back(std::make_unique<B>());
v.emplace_back(std::make_unique<C>());
for(const auto& p : v)
{
visit(p.get(), [](const A*){ std::cout << "A"; },
[](const B*){ std::cout << "B"; },
[](const C*){ std::cout << "C"; });
}
}
Run Code Online (Sandbox Code Playgroud)
ABC
| 归档时间: |
|
| 查看次数: |
611 次 |
| 最近记录: |