我对std :: tuple和std :: tie相当新。我需要一种方法来根据从左到右的比较顺序有效地排序结构。出于这个原因,我选择使用std::make_tuple和std::tie类型在提供的live example中自定义订购StructA。元组方法提供从左到右开始的内置等价比较,这对于带有伴随lambda比较器的std :: sort的 LessThanComparable 元素排序是理想的(我为此展示了3个示例)。
问题在于,据我所知,std::make_tuple生成元组元素的inefficient copies,我想知道是否有某种方法将std :: make_tuple与std :: tie结合起来正如我试图用我的第三个比较器 - 失败(否则它的输出看起来像第一个输出排序)。
在我的具体示例中,我不能直接使用std::tie,因为我需要使用临时作为元组中的第一个元素。
输出如下
Order[mPath.filename(), elem1, intVal]
======================================
"/zoo/dir1/filename.txt" - nameA1 - 1
"/tmp/dir1/filename.txt" - nameA1 - 3
"/fad/dir1/filename.txt" - nameA1 - 4
Order[mPath, elem1, intVal]
======================================
"/fad/dir1/filename.txt" - nameA1 - 4
"/tmp/dir1/filename.txt" - nameA1 - 3
"/zoo/dir1/filename.txt" - nameA1 - 1
Order[mPath.filename(), elem1, intVal]
======================================
"/fad/dir1/filename.txt" - nameA1 - 4
"/tmp/dir1/filename.txt" - nameA1 - 3
"/zoo/dir1/filename.txt" - nameA1 - 1
我期待第三组输出与第一组相同,或者如果有人可以告诉我如何正确地将低效的std :: tuples与高效的std :: ties混合
#include <iostream>
#include <string>
#include <vector>
#include <tuple>
#include <boost/filesystem.hpp>
struct StructA {
boost::filesystem::path mPath;
std::string elem1;
int intVal;
};
template<typename CharT, typename Traits>
std::basic_ostream<CharT, Traits>&
operator<<(std::basic_ostream<CharT, Traits>& os, StructA const& sa) {
return os << sa.mPath << " - " << sa.elem1 << " - " << sa.intVal << std::endl;
}
int main()
{
std::vector<StructA> aStructs = {
{"/zoo/dir1/filename.txt", "nameA1", 1},
{"/fad/dir1/filename.txt", "nameA1", 4},
{"/tmp/dir1/filename.txt", "nameA1", 3}
};
std::cout << "Order[mPath.filename(), elem1, intVal]" << std::endl;
std::cout << "======================================" << std::endl;
std::sort(aStructs.begin(), aStructs.end(),
[](const StructA& lhs, const StructA& rhs){
return std::make_tuple(lhs.mPath.filename(), lhs.elem1, lhs.intVal) <
std::make_tuple(rhs.mPath.filename(), rhs.elem1, rhs.intVal);
});
// print reordered structs
std::copy(aStructs.begin(), aStructs.end(),
std::ostream_iterator<StructA>(std::cout, ""));
std::cout << std::endl;
std::cout << "Order[mPath, elem1, intVal]" << std::endl;
std::cout << "======================================" << std::endl;
std::sort(aStructs.begin(), aStructs.end(),
[](const StructA& lhs, const StructA& rhs){
return std::tie(lhs.mPath, lhs.elem1, lhs.intVal) <
std::tie(rhs.mPath, rhs.elem1, rhs.intVal);
});
// print reordered structs
std::copy(aStructs.begin(), aStructs.end(),
std::ostream_iterator<StructA>(std::cout, ""));
std::cout << std::endl;
std::cout << "Order[mPath.filename(), elem1, intVal]" << std::endl;
std::cout << "======================================" << std::endl;
std::sort(aStructs.begin(), aStructs.end(),
[](const StructA& lhs, const StructA& rhs){
// attempt at efficiency - but not quite right
return lhs.mPath.filename() < rhs.mPath.filename() &&
std::tie(lhs.elem1, lhs.intVal) < std::tie(rhs.elem1, rhs.intVal);
});
// print reordered structs
std::copy(aStructs.begin(), aStructs.end(),
std::ostream_iterator<StructA>(std::cout, ""));
}
答案 0 :(得分:3)
std::tuple<std::string, std::string const&, int>
sort_helper(StructA const& s){
return{ s.mPath.filename(), s.elem1, s.intVal };
}
然后:
std::sort(aStructs.begin(), aStructs.end(),
[](const StructA& lhs, const StructA& rhs){
return sort_helper(lhs)<sort_helper(rhs);
}
);
哪个看起来更干净?
基本上以std::string
一次移动为代价。
答案 1 :(得分:1)
我刚刚发现第3个lambda的问题在于我没有正确地比较元素以进行等价和词典比较。对于tuple comparison,子弹3)中概述了正确的方法,其中表明我应该使用以下方法。
3)(bool)(std :: get&lt; 0&gt;(lhs)&lt; std :: get&lt; 0&gt;(rhs))|| (!(bool)(std :: get&lt; 0&gt;(rhs)&lt; std :: get&lt; 0&gt;(lhs))&amp;&amp; lhstail&lt; rhstail), 其中lhstail是没有第一个元素的lhs,而rhstail是rhs 没有它的第一个元素。对于两个空元组,返回false。
固定的lambda比较器,用于根据文件名()临时排序第一个排序,然后使用高效的std :: tie作为元组中的其他元素
std::cout << "Order[mPath.filename(), elem1, intVal]" << std::endl;
std::cout << "======================================" << std::endl;
std::sort(aStructs.begin(), aStructs.end(),
[](const StructA& lhs, const StructA& rhs){
// attempt at efficiency - but not quite right
// AHA, I think I figured it out - see tuple operator_cmp
// return value documentation which states
// (bool)(std::get<0>(lhs) < std::get<0>(rhs)) ||
// (!(bool)(std::get<0>(rhs) < std::get<0>(lhs)) &&
// lhstail < rhstail), where lhstail is lhs without
// its first element, and rhstail is rhs without its
// first element. For two empty tuples, returns false.
// --------------------------------------------------------
// edit thanks to @Praetorian for suggesting the following:
// --------------------------------------------------------
auto f1 = lhs.mPath.filename(); auto f2 = rhs.mPath.filename();
return std::tie(f1, lhs.elem1, lhs.intVal) < std::tie(f2, rhs.elem1, rhs.intVal);
});
这样做的第一组结果与第3组完全相同 - 对于filename()暂时不是非常有效,但至少我没有对我的struct中的所有元素进行std :: make_tuple命中。更新的实时示例为here