如何从对象的数据成员构造 std::set 或 Boost flat_set?
How to construct a std::set or Boost flat_set from objects’ data members?
#include <iostream>
#include <vector>
#include <set>
class Myclass
{
int member_a;
int member_b;
public:
Myclass() {};
Myclass(int a_init, int b_init) : member_a(a_init), member_b(b_init) {};
operator int() const { return member_a; }
int get_a() const { return member_a; }
};
int main()
{
auto myvector = std::vector<Myclass>({ {1, 0}, {2, 0}, {2, 0}, {3, 0} });
auto myset = std::set<int>(myvector.begin(), myvector.end());
for (auto element : myset) {
std::cout << "element: " << element << "\n";
}
}
如您所见,我正在构造一个 std::set
,它只包含 std::vector
中每个对象的一个特定数据成员。我通过使用 operator int()
实现了这一点。
但是,我不喜欢这个解决方案,因为它的可读性不是很好并且会产生潜在的陷阱,而且我可能还想创建一组只有 member_b
s.
有没有一种方法可以在不使用循环的情况下使用 get_a()
而不是 operator int()
来构建集合?我还想避免创建仅包含 member_a
的临时向量。
同样的问题与构建 Boost::flat_set
特别相关,据我所知,如果在循环中逐一添加元素,将不必要地重新排序。
您可以使用std::transform
to insert the desired members to myset
instead of using operator int()
. (See live online)
#include <algorithm> // std::transform
#include <iterator> // std::inserter
std::transform(myvector.cbegin(), myvector.cend()
, std::inserter(myset, myset.begin())
, [](const auto& cls) { return cls.get_a(); }
);
足够通用?好的,为了更通用,你可以把它放到一个函数中,在函数中传递Myclass
的vector,myset
被填充,并且需要调用的成员函数指针。 (See live online)
#include <algorithm> // std::transform
#include <iterator> // std::inserter
#include <functional> // std::invoke
#include <utility> // std::forward
using MemFunPtrType = int(Myclass::*)() const; // convenience type
void fillSet(const std::vector<Myclass>& myvector, std::set<int>& myset, MemFunPtrType func)
{
std::transform(myvector.cbegin(), myvector.cend()
, std::inserter(myset, myset.begin())
, [func](const Myclass& cls) {
return (cls.*func)();
// or in C++17 simply invoke the func with each instace of the MyClass
// return std::invoke(func, cls);
}
);
}
或完全通用使用 templates, one could: (See live online)
template<typename Class, typename RetType, typename... Args>
void fillSet(const std::vector<Class>& myvector
, std::set<RetType>& myset
, RetType(Class::*func)(Args&&...)const
, Args&&... args)
{
std::transform(myvector.cbegin(), myvector.cend()
, std::inserter(myset, myset.begin())
, [&](const Myclass& cls) { return std::invoke(func, cls, std::forward<Args>(args)...); }
);
}
现在你填写myset
个赞。
fillSet(myvector, myset, &Myclass::get_a); // to fill with member a
fillSet(myvector, myset, &Myclass::get_b); // to fill with member b
这是完整的工作示例:
#include <iostream>
#include <vector>
#include <set>
#include <algorithm> // std::transform
#include <iterator> // std::inserter
#include <functional> // std::invoke
#include <utility> // std::forward
class Myclass
{
int member_a;
int member_b;
public:
Myclass(int a_init, int b_init) : member_a{ a_init }, member_b{ b_init } {};
int get_a() const noexcept { return member_a; }
int get_b() const noexcept { return member_b; }
};
template<typename Class, typename RetType, typename... Args>
void fillSet(const std::vector<Class>& myvector
, std::set<RetType>& myset
, RetType(Class::*func)(Args&&...)const
, Args&&... args)
{
std::transform(myvector.cbegin(), myvector.cend()
, std::inserter(myset, myset.begin())
, [&](const Myclass& cls) { return std::invoke(func, cls, std::forward<Args>(args)...); }
);
}
int main()
{
auto myvector = std::vector<Myclass>({ {1, 0}, {2, 0}, {2, 0}, {3, 0} });
std::set<int> myset;
std::cout << "Filling with member a\n";
fillSet(myvector, myset, &Myclass::get_a);
for (auto element : myset) std::cout << "element: " << element << "\n";
std::cout << "Filling with member b\n";
myset.clear();
fillSet(myvector, myset, &Myclass::get_b);
for (auto element : myset) std::cout << "element: " << element << "\n";
}
输出:
Filling with member a
element: 1
element: 2
element: 3
Filling with member b
element: 0
类似于std::transform
,您也可以使用boost::transform
(或者它的管道变体boost::adaptors::transformed
),它获取整个容器,而不是开始和结束迭代器。它 returns 一个视图,你可以用它初始化 myset
。
auto view = boost::transform(myvector, std::mem_fn(&myclass::get_a));
auto myset = std::set<int>(view.begin(), view.end());
您还可以使用 boost::copy_range
从右值范围构建容器。
auto to_a = std::mem_fn(&myclass::get_a);
auto myset = boost::copy_range<std::set<int>>(myvector | boost::adaptors::transformed(to_a));
#include <iostream>
#include <vector>
#include <set>
class Myclass
{
int member_a;
int member_b;
public:
Myclass() {};
Myclass(int a_init, int b_init) : member_a(a_init), member_b(b_init) {};
operator int() const { return member_a; }
int get_a() const { return member_a; }
};
int main()
{
auto myvector = std::vector<Myclass>({ {1, 0}, {2, 0}, {2, 0}, {3, 0} });
auto myset = std::set<int>(myvector.begin(), myvector.end());
for (auto element : myset) {
std::cout << "element: " << element << "\n";
}
}
如您所见,我正在构造一个 std::set
,它只包含 std::vector
中每个对象的一个特定数据成员。我通过使用 operator int()
实现了这一点。
但是,我不喜欢这个解决方案,因为它的可读性不是很好并且会产生潜在的陷阱,而且我可能还想创建一组只有 member_b
s.
有没有一种方法可以在不使用循环的情况下使用 get_a()
而不是 operator int()
来构建集合?我还想避免创建仅包含 member_a
的临时向量。
同样的问题与构建 Boost::flat_set
特别相关,据我所知,如果在循环中逐一添加元素,将不必要地重新排序。
您可以使用std::transform
to insert the desired members to myset
instead of using operator int()
. (See live online)
#include <algorithm> // std::transform
#include <iterator> // std::inserter
std::transform(myvector.cbegin(), myvector.cend()
, std::inserter(myset, myset.begin())
, [](const auto& cls) { return cls.get_a(); }
);
足够通用?好的,为了更通用,你可以把它放到一个函数中,在函数中传递Myclass
的vector,myset
被填充,并且需要调用的成员函数指针。 (See live online)
#include <algorithm> // std::transform
#include <iterator> // std::inserter
#include <functional> // std::invoke
#include <utility> // std::forward
using MemFunPtrType = int(Myclass::*)() const; // convenience type
void fillSet(const std::vector<Myclass>& myvector, std::set<int>& myset, MemFunPtrType func)
{
std::transform(myvector.cbegin(), myvector.cend()
, std::inserter(myset, myset.begin())
, [func](const Myclass& cls) {
return (cls.*func)();
// or in C++17 simply invoke the func with each instace of the MyClass
// return std::invoke(func, cls);
}
);
}
或完全通用使用 templates, one could: (See live online)
template<typename Class, typename RetType, typename... Args>
void fillSet(const std::vector<Class>& myvector
, std::set<RetType>& myset
, RetType(Class::*func)(Args&&...)const
, Args&&... args)
{
std::transform(myvector.cbegin(), myvector.cend()
, std::inserter(myset, myset.begin())
, [&](const Myclass& cls) { return std::invoke(func, cls, std::forward<Args>(args)...); }
);
}
现在你填写myset
个赞。
fillSet(myvector, myset, &Myclass::get_a); // to fill with member a
fillSet(myvector, myset, &Myclass::get_b); // to fill with member b
这是完整的工作示例:
#include <iostream>
#include <vector>
#include <set>
#include <algorithm> // std::transform
#include <iterator> // std::inserter
#include <functional> // std::invoke
#include <utility> // std::forward
class Myclass
{
int member_a;
int member_b;
public:
Myclass(int a_init, int b_init) : member_a{ a_init }, member_b{ b_init } {};
int get_a() const noexcept { return member_a; }
int get_b() const noexcept { return member_b; }
};
template<typename Class, typename RetType, typename... Args>
void fillSet(const std::vector<Class>& myvector
, std::set<RetType>& myset
, RetType(Class::*func)(Args&&...)const
, Args&&... args)
{
std::transform(myvector.cbegin(), myvector.cend()
, std::inserter(myset, myset.begin())
, [&](const Myclass& cls) { return std::invoke(func, cls, std::forward<Args>(args)...); }
);
}
int main()
{
auto myvector = std::vector<Myclass>({ {1, 0}, {2, 0}, {2, 0}, {3, 0} });
std::set<int> myset;
std::cout << "Filling with member a\n";
fillSet(myvector, myset, &Myclass::get_a);
for (auto element : myset) std::cout << "element: " << element << "\n";
std::cout << "Filling with member b\n";
myset.clear();
fillSet(myvector, myset, &Myclass::get_b);
for (auto element : myset) std::cout << "element: " << element << "\n";
}
输出:
Filling with member a
element: 1
element: 2
element: 3
Filling with member b
element: 0
类似于std::transform
,您也可以使用boost::transform
(或者它的管道变体boost::adaptors::transformed
),它获取整个容器,而不是开始和结束迭代器。它 returns 一个视图,你可以用它初始化 myset
。
auto view = boost::transform(myvector, std::mem_fn(&myclass::get_a));
auto myset = std::set<int>(view.begin(), view.end());
您还可以使用 boost::copy_range
从右值范围构建容器。
auto to_a = std::mem_fn(&myclass::get_a);
auto myset = boost::copy_range<std::set<int>>(myvector | boost::adaptors::transformed(to_a));