我应该避免在这里使用指针吗?
Should I avoid using pointers here?
我有这个简单的代码:
std::vector<std::map<double,double>> v;
//populate v
//we know each map already has correct key order (enforced by c++)
//but i also want to make sure the different maps have correct key order
//this is how I do it using a pointer:
const double *last_key = nullptr;
for (const auto &map : v)
{
if (map.size() > 0) //ignore empty maps
{
if (last_key)
{
const auto &new_key = map.cbegin()->first;
if (!(*last_key < new_key))
throw std::runtime_error("invalid key order");
}
last_key = &(--map.cend())->first;
}
}
这是指针的好用处吗?你会怎么做?
我知道的唯一真正的选择(如果我想避免指针(是这样做:
double last_key;
bool last_key_has_been_set = false;
这有效,但它要求密钥是默认可构造的,并且涉及不必要的密钥复制(与double
不同的密钥类型存在问题(。
好的,既然我现在(认为我(明白了你的代码是关于什么的,这是我对它的看法:
auto iter = v.begin();
auto end = v.end();
while (iter != end && iter->empty())
++iter;
if (iter != end)
{
while (true) // loop and a half
{
auto next = iter+1; // at this point, we know iter != end
while (next != end && next->empty())
++next;
if (next == end)
break;
auto lhslast = lhs.end();
--lhslast;
if (lhslast->first > next->begin()->first)
throw std::runtime_error("invalid key order");
iter = next;
}
}
编辑:
上面的代码可以使用另一种算法进一步改进:
取代
while (iter != end && iter->empty())
++iter;
跟
iter = std::find_if(iter, end,
[](std::map<double, double> const& m) { return m.empty(); });
类似于next
循环。
另一种选择是注意,如果它不是空地图,您可以只使用 adjacent_find
.因此,另一种选择是利用 Boost 的filter_iterator
来摆脱空地图。因此做
#include <boost/iterator/filter_iterator.hpp>
struct is_not_empty
{
template<typename Container> bool operator()(Container const& c) const
{
return !c.empty();
}
};
然后在您的代码位置
auto fbegin = boost::make_filter_iterator(is_not_empty(), v.begin(), v.end());
auto fend = boost::make_filter_iterator(is_not_empty(), v.end(), v.end());
if (std::adjacent_find(fbegin, fend,
[](std::map<double, double> const& lhs,
std::map<double, double> const& rhs) -> bool
{
auto lhslast = lhs.end();
--lhslast;
return lhslast->first > rhs.begin()->first;
}) != fend)
throw std::runtime_error("invalid key order");
筛选器迭代器确保仅考虑非空映射。
我认为标准库中没有合适的预定义算法可以做到这一点。特别是,如果你要为它定义一个相对复杂和有状态的谓词,std::adjacent_find
可以用于此,但这实际上相当于滥用std::adjacent_find
作为std::for_each
的某种替代品,即它与std::adjacent_find
的原始目的没有太大关系。
但是,您应该使用迭代器,而不是裸指针。我还建议将检查代码放入一个单独的函数中,可能名为 check
。这是我的建议:
#include <vector>
#include <map>
#include <iostream>
bool check(const std::vector<std::map<double,double>> &v)
{
/* Fast-forward to the first non-empty entry. */
auto it = begin(v);
for( ; it != end(v) ; ++it)
if (!it->empty())
break;
/* We might be done by now. */
if (it == end(v))
return true;
/* Else, go through the remaining entries,
skipping empty maps. */
auto prev = it->end();
advance(prev,-1);
++it;
for ( ; it != end(v) ; ++it)
{
if (!it->empty())
{
if (it->begin()->first < prev->first)
return false;
prev = it->end();
advance(prev,-1);
}
}
return true;
}
int main()
{
std::vector<std::map<double,double>> v;
/* Two entries for the vector, invalid order. */
v.push_back({ {1.0,1.0} , {2.0,4.0} });
v.push_back({ {3.0,9.0} , {1.0,16.0} });
if (!check(v))
throw std::runtime_error("Invalid order of the maps in the vector.");
return 0;
}
注意:如果要将check
函数定义为采用一系列迭代器而不是对容器的引用作为参数的算法,它将更加C++(或者至少更像标准库中的算法(。重写函数以匹配此概念非常简单。
注意 2:使用迭代器而不是裸指针的优点是,您可以更好、更清晰地抽象出所需的内容:引用映射中项目的内容,而double*
指针可以指向各种事物。但是,使用迭代器也有一个缺点:如果要修改算法,使其在遍历向量时更改映射,则迭代器可能会失效,而指针不会(除非您删除它指向的元素(。(但是,如果您更改矢量,指针可能会失效。
但是,只要检查过程仅用于检查而不用于其他任何目的(我的代码通过将代码放入专用于此目的的单独函数中,并将向量作为常量引用来指示(,迭代器失效就不是问题。
使用 C++1y 功能(std::tr2::optional
(,但应该适用于任何容器和容器元素的任何排序:
struct compare_key_order {
template<typename LHS, typename RHS>
bool operator()( LHS const& lhs, RHS const& rhs ) {
return lhs.first < rhs.first;
}
};
template<typename ContainerOfContainers, typename Ordering>
bool are_container_endpoints_ordered( ContainerOfMaps&& meta, Ordering&& order=compare_key_order() )
{
using std::begin; using std::end;
// or boost::optional:
std::tr2::optional< decltype( begin(begin(meta)) ) > last_valid;
for( auto&& Map : std::forward<Meta>(meta) ) {
auto b = begin(Map);
auto e = end(Map);
if (b==e)
continue;
if (last_valid)
if (!order( **last_valid, *b ))
return false;
last_valid = e;
}
return true;
}
optional
是一种更漂亮、更不容易出错的处理"此元素可能存在或不存在"的方法,而不是指针可以nullptr
。 如果您正在使用boost
或有权访问std::tr2::optional
(或者您将来正在阅读本文,如果存在std::optional
(,则比指针更好。
您还可以将"是否有last_valid
移出状态并移动到程序代码位置:
struct compare_key_order {
template<typename LHS, typename RHS>
bool operator()( LHS const& lhs, RHS const& rhs ) {
return lhs.first < rhs.first;
}
};
template<typename ContainerOfContainers, typename Ordering>
bool are_container_endpoints_ordered( ContainerOfMaps&& meta, Ordering&& order=compare_key_order() )
{
using std::begin; using std::end;
auto it = begin(meta);
while( it != end(meta) && (begin(*it) == end(*it)) {
++it;
}
if ( it == end(meta) )
return true;
auto last_valid_end = end(*it);
for( ++it; it != end(meta); ++it ) {
auto b = begin(*it);
auto e = end(*it);
if (b==e)
continue;
if (!order( *last_valid_end, *b ))
return false;
last_valid = e;
}
return true;
}
这将允许相同的算法在向量对向量上运行,甚至检查向量向量是否具有排序的端点(具有不同的order
(。
最好的注释给出了答案:使用 adjacent_find
。
首先有点逻辑。如果有 n 个
你可以用荒谬推理来证明这一点:如果没有这样的i,那么对于n和m之间的所有i,我们都有秩序,因为序关系是传递的,key[n] <= key[m]:荒谬。这意味着,忽略空映射,如果您的键顺序错误,那么您有两个相邻的键顺序错误。
所以你的算法应该是:
typedef map<double, double> map_t;
vector<map_t> v;
remove_if(v.begin(), v.end(), [](map_t const& m){return m.empty();});
if(adjacent_find(v.begin(), v.end(), [](map_t const& l, map_t const& r)
{
return (--l.cend())->first > r.cbegin()->first;
}) != v.end())
throw std::runtime_error("invalid key order");
当然,这是如果您可以先从矢量中删除空地图。(我们可以假设,因为空地图可能没有那么有意义,但这肯定取决于整个情况(。
- 努力将整数转换为链表。不知道我在这里做错了什么
- 对象指针在c++中是如何工作的
- 这个指针在c++中的用法
- 我可以在这里替换什么,因为我不能在 C# 中使用隐式变量的 lambda 函数?
- 使用结构成员指针在C++中填充结构
- 当我从下面的代码中删除关键字 virtual 时,它可以正常工作,否则会出现错误。在这里"virtual"字的意义是什么?
- File.cpp.o:OpenPose 标志 CMakeFiles/.. 的多重定义/main.cpp.o:首先在这里定
- C++指针在 for 循环中被覆盖
- 为什么thread_local变量在这里从未初始化?
- 为什么我必须在这里使用dynamic_cast
- 带有 cout 的指针在C++
- 为什么我们在这里将 0 添加到双空指针上?
- 为什么两个指向相同链接的两个指针在这里表现不同
- 这个指针增量操作在这里实现了什么
- 我在这里滥用了指针吗?虽然循环条件似乎无法正常工作
- 我应该避免在这里使用指针吗?
- 链表分割错误。我猜我在这里的指针做错了什么
- 为什么在这里返回一个 malloc'd 指针会导致免费"HEAP CORRUPTION"?
- 我必须在这里使用指针吗
- 为什么指针算术不能在这里工作