我一直在试验我称之为PolymorphicVariant
的类,这类似于unique_ptr
,除了它将其值存储在内联而不是在堆上分配它。为此我使用了variant
类型(自制版本)和一些静态断言来检查所有变体是否实现了给定的接口(即它们都来自相同的基类)。然后我有方法来获取指向基类的指针。然后我可以将PolymorphicVariant
存储在容器中,并使其行为与存储指向基类的指针相同,除非没有间接。
template <typename Base, typename... Derivees>
class PolymorphicVariant {
static_assert(std::is_polymorphic<Base>{},
"Base type has to be polymorphic");
static_assert(meta::And<std::is_base_of<Base, Derivees>...>{},
"Derivees have to derive from Base");
using VariantType = Variant<Derivees...>;
using Types = meta::List<Derivees...>;
struct ToBasePtrVisitor {
template <typename T>
auto operator()(const T& value) const -> const Base* {
return &value;
}
};
public:
// ... Constructors and assignment operators ...
auto get() const -> const Base* {
return apply(ToBasePtrVisitor{}, m_storage);
}
private:
VariantType m_storage;
};
然后我可以做这样的事情:
// Bar and Baz both implement IFoo
using Foo = PolymorphicVariant<IFoo, Bar, Baz>;
std::vector<Foo> foos;
for (int i = 0; i < 10; ++i) {
if (i % 2 == 0) {
foos.emplace_back(Bar{});
} else {
foos.emplace_back(Baz{});
}
}
for (const auto& elem : foos) {
elem.get()->some_virtual_fn();
}
所以现在我的问题。让我们说我有一个函数引用PolymorphicVariant
(就像使用指向抽象基类的函数一样):
auto mutate_foo(Foo& foo) -> void {
foo.get()->mutating_function();
}
Bar my_bar;
mutate_foo(my_bar); // This doesn't work
这不起作用的原因是我无法将Bar
的引用绑定到Foo
的引用(即PolymorphicVariant<IFoo, Bar, Baz>
)。有没有办法让这成为可能?某种方式告诉编译器Bar
引用实际上可以被视为Foo
引用?无需更改IFoo
或Bar
的实施。如果不可能,我不得不将mutate_foo
的签名更改为mutate_foo(IFoo* foo) -> void
,但这会让我觉得不那么干净,因为我想要Foo
作为首选类型来表示&#34;包含满足界面IFoo&#34;的东西。谢谢!
答案 0 :(得分:0)
有没有办法让这成为可能?无需更改
的实施IFoo
或Bar
。
我现在想不出有任何办法。如果我能想到一个(好的),我会编辑我的帖子。
我希望
Foo
充当首选类型,用于表示“包含符合接口IFoo
的内容的内容”。
您可以使用template
功能:
template <typename TFoo>
auto mutate_foo(TFoo& foo) -> void {
foo.get()->mutating_function();
}
您可能希望使用谓词来约束它,该谓词检查不是TFoo
是否实际实现了您想要的界面:
template <typename TFoo,
typename std::enable_if_t<implements_foo_interface<TFoo>{}>>
auto mutate_foo(TFoo& foo) -> void {
foo.get()->mutating_function();
}
implements_foo_interface
可以使用detection idiom轻松实现。
答案 1 :(得分:0)
在评论之后,我发现Foo
和Bar
不需要从公共接口继承。如果在Foo
和Bar
的名称空间中提供自由函数重载,则变体访问者已经提供了管道以找到正确的方法。
示例(使用boost::variant
,因为我无法访问您的库):
#include <boost/variant.hpp>
#include <memory>
#include <iostream>
struct Foo {
void do_something() { std::cout << "something\n"; }
};
struct Bar {
// note - dissimilar interfaces
void do_something_else() { std::cout << "something else\n"; }
};
void mutating_function(Foo& on) {
// overload explicit actions here
on.do_something();
}
void mutating_function(Bar& on) {
// overload explicit actions here
on.do_something_else();
}
struct invoke_mutating_function
{
template<class Target>
void operator()(Target& on) const {
mutating_function(on);
}
};
using FooBar = boost::variant<Foo, Bar>;
void mutating_function(FooBar& on)
{
boost::apply_visitor(invoke_mutating_function(), on);
}
int main()
{
FooBar x = Foo();
FooBar y = Bar();
mutating_function(x);
mutating_function(y);
}
预期产出:
something
something else
答案 2 :(得分:0)
是
下面:
int m = Integer.parseInt( args[0] );
if(m>=0&&m<13)
System.out.println( months[ m ] );
创建一个类型auto mutate_foo(Foo& foo) -> void {
foo.get()->mutating_function();
}
。
FooRef
auto mutate_foo(FooRef foo) -> void {
foo.get()->mutating_function();
}
基于FooRef
。 PolymorphicAnyRef
与您的PolymorphicAnyRef
非常相似,但它会将指针存储到源自PolymorphicVariant
的内容中,以及获取Base
的方式从那个指针。
一个简单的实现是简单地存储Base*
并让Base*
返回它。
get()
这会增加一些语法糖而不是template<class Base>
struct PolymorphicAnyRef {
Base* ptr = 0;
Base* get() const { return ptr; }
// blah, etc
template<class T>
PolymorphicAnyRef( T& t ): ptr(std::addressof(t)) {}
template<class...Ts>
PolymorphicAnyRef( PolymorphicVariant<Base, Ts...>& pv ):
ptr( pv.get() )
{}
};
,因为它会从Base*
或任何引用转换为Base
。
顺便说一下,我写了PolymorphicVariant<Base,???>
而不是PolymorphicVariant
来保证存储PolymorphicAny<Base, Ts...>
中的任何一个,但也可以存储小于和较少对齐 Ts...
足够“常规”(可复制,无论你需要什么)。派生检查是在构造而不是类型实例化时完成的。
然后它可以存储一个函数指针,将Ts...
转换为存储的内部void*
到Any
,并将其用于Base*
。一个支持添加新类型擦除方法的写得很好的get()
可以让你将该操作与默认操作一起存储。
但我有点疯了。