[英]polymorphic vector without virtual or inheritance
I am trying to implement a vector that can take elements of several types, and can apply a function on all of them. 我正在尝试实现一个向量,该向量可以采用多种类型的元素,并且可以对所有元素应用函数。 This is easily done with a base class, virtual functions and inheritance, but I explicity do not want to use it. 使用基类,虚函数和继承很容易做到这一点,但是我明确地不想使用它。 Here is how far I am so far: 这是我到目前为止的距离:
#include <iostream>
#include <vector>
#include <tuple>
// this will be my new polymorphic vector;
template<typename... Ts>
class myvector {
std::tuple<std::vector<Ts>...> vectors;
template <template<typename> class funtype>
void for_each() {
}
template <template<typename> class funtype, typename X, typename... Xs>
void for_each() {
std::vector<X>& vector = std::get<std::vector<X>>(vectors);
for ( X& x : vector ) {
funtype<X> fun;
fun(x);
}
for_each<funtype, Xs...>();
}
public:
template <typename T>
void push_back(const T& t) {
std::vector<T>& vector = std::get<std::vector<T>>(vectors);
vector.push_back(t);
}
template <typename T>
void pop_back() {
std::vector<T>& vector = std::get<std::vector<T>>(vectors);
vector.pop_back();
}
/* here I would like to pass a function, or function object that
* can be expanded to all underlying types. I would prefer to just
* give a function name, that has an implementation to all types in Ts
*/
template <template<typename> class funtype>
void ForEach() {
for_each<funtype,Ts...>();
}
};
struct foo {
};
struct bar {
};
template <typename T>
void method(T& t);
template<>
void method(foo& b) {
std::cout << "foo" << std::endl;
}
template<>
void method(bar& b) {
std::cout << "bar" << std::endl;
}
int main()
{
myvector<foo,bar> mv;
mv.push_back( foo{} );
mv.push_back( bar{} );
mv.ForEach<method>();
}
at the moment I am kind of stuck, I hope you can give me some advise on how to go further. 目前,我感到有些困惑,希望您能给我一些进一步建议。
A common solution is to use a function object with a set of operator()
: 常见的解决方案是将函数对象与一组operator()
:
struct my_fun_type
{
void operator()(foo&) const
{ std::cout << "foo\n"; }
void operator()(bar&) const
{ std::cout << "bar\n"; }
};
This allows to pass a "set" of overloaded functions to an algorithm, state, and is rather convenient to use: 这允许将“重载函数”的“集合”传递给算法,状态,并且使用起来相当方便:
my_algorithm(my_fun_type{});
If we want to add support for such function objects, we could define ForEach
as follows: 如果要添加对此类功能对象的支持,可以按以下方式定义ForEach
:
template <typename Elem, typename Fun>
void for_each(Fun&& fun) {
std::vector<Elem>& vector = std::get<std::vector<Elem>>(vectors);
for ( Elem& e : vector ) {
fun(x);
}
}
template <typename Fun>
void ForEach(Fun&& fun) {
int dummy[] = { 0, (for_each<Ts>(fun), 0)... };
(void)dummy;
}
That dummy
is a trick to call for_each
for all types in Ts
. 这个dummy
是为Ts
所有类型调用for_each
的技巧。 The (void)dummy
is intended to suppress a compiler warning ( dummy
is never read from). (void)dummy
用于禁止编译器警告(永远不会读取dummy
)。
You can learn more about this technique in other Q&As, such as that one . 您可以在其他问答中进一步了解此技术,例如该问答。
The Fun&&
is not an rvalue reference, but a universal reference . Fun&&
不是右值引用,而是通用引用 。
Note that the above example differs from many Standard Library algorithms, which take the function object by value: 请注意,上面的示例与许多标准库算法不同,后者按值获取函数对象:
template <typename Elem, typename Fun>
void for_each(Fun fun) {
std::vector<Elem>& vector = std::get<std::vector<Elem>>(vectors);
std::for_each(vector.begin(), vector.end(), std::move(fun));
}
template <typename Fun>
void ForEach(Fun fun) {
int dummy[] = { 0, (for_each<Ts>(fun), 0)... };
(void)dummy;
}
To pass a set of overloaded free functions, we can wrap them in a function object (thank @Yakk for the suggestion): 要传递一组重载的自由函数,我们可以将它们包装在一个函数对象中(感谢@Yakk的建议):
struct method_t
{
template<class... Ts>
void operator()(Ts&&... ts) const
{ method( std::forward<Ts>(ts)... ); }
};
In C++1y, such a function object type can be created with less boilerplate using a polymorphic lambda: 在C ++ 1y中,使用多态lambda可以用更少的样板创建这样的函数对象类型:
[](auto&&... pp)
{ method( std::forward<decltype(pp)>(pp)... ); }
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.