【问题标题】:Checking if iterator is not assigned to item when there is no specific container在没有特定容器时检查迭代器是否未分配给项目
【发布时间】:2016-12-12 23:06:34
【问题描述】:

我想在两个std::vector 中搜索一个值。如果在其中一个中找到它,我想返回它的迭代器。如果没有,我想返回一些表明它没有找到的值。

在只涉及一个std::vector 的正常情况下,我会返回std::vector::end。在这种情况下我该怎么办?

正常情况:

auto find_ten=[](const std::vector<int>& v){
    return std::find(v.cbegin(),v.cend(),10);
}

我的情况:

auto find_ten=[](const std::vector<int>& v1,const std::vector<int>& v2){
    auto it1=std::find(v1.cbegin(),v1.cend(),10);
    if(it1==v1.cend()){
        auto it2=std::find(v2.cbegin(),v2.cend(),10);
        if(it2==v2.cend()){
             //What should I return here??
        }
        return it2;
    }
    return it1;
}

我想返回一些我可以稍后检查的东西,以知道在其中任何一个中都找不到数字 10

【问题讨论】:

  • 迭代器从不为“null”。
  • (虽然一个单一的迭代器可以帮助你,但你可以用一个迭代器做的很少。)
  • @LightnessRacesinOrbit 已更正
  • 我将返回数组中的索引,如果未找到则返回 -1。这可以通过区分begin() 和迭代器指针来完成。但也许这很蹩脚。
  • @Jean-FrançoisFabre 返回索引意味着您必须返回它属于哪个向量的线索

标签: c++ c++11 vector


【解决方案1】:

从 C++14 开始,您可以比较值初始化的迭代器,如果它们满足 ForwardIterator 类别或更强(参见 [forward.iterators] 第 2 段)。值初始化的迭代器等价于空指针,因此您可以使用:

    if(it2==v2.cend()){
         return std::vector<int>::iterator{};
    }

然后调用者可以这样做:

std::vector<int>::iterator not_found{};
auto find_ten = ...
if (find_ten != not_found)
{
  ...
}

【讨论】:

  • 谢谢.. 这似乎正是我想要的。但我还不能使用 C++ 14。
  • 实际上它可能适用于任何版本的 C++(迭代器调试模式除外),C++14 标准刚刚使其正式合法
  • 啊那太好了
  • 直到 c++14,只需声明一个私有向量 foo 并且 not_found() 返回 end(foo)。这将成为您的哨兵价值
  • 但是您是否可以将值初始化的迭代器与“正常”迭代器进行实际比较(在“找到”的情况下)? N3644 实际上说这是讨论中的 UB(“将值初始化的迭代器与具有非奇异值的迭代器进行比较的结果是未定义的。”)。
【解决方案2】:
auto find_ten=[](const std::vector<int>& v1,const std::vector<int>& v2){
    auto it1=std::find(v1.cbegin(),v1.cend(),10);
    if(it1==v1.cend()){
        auto it2=std::find(v2.cbegin(),v2.cend(),10);        
        return it2; // whether it's end or not
    }
    return it1;
}

使用时,只需测试(rval != v2.end())(rval 是返回的迭代器)。我知道,这不是很对称。

将布尔值作为out 参数传递

auto find_ten=[](const std::vector<int>& v1,const std::vector<int>& v2, bool &found){
    found = true; // suppose we will find it

    auto it1=std::find(v1.cbegin(),v1.cend(),10);
    if(it1==v1.cend()){
        auto it2=std::find(v2.cbegin(),v2.cend(),10);  
        found = it2 != v2.end();     // false if not in v1 or in v2
        return it2; // whether it's end or not
    }

    return it1;
}

【讨论】:

  • 真的吗?比较来自不同容器的迭代器是否定义明确?根据我的经验,for (auto it = contA.begin(), end = contB.end(); it != end; ++it) 并不顺利。
  • 我同意这不是很学术,因此我的替代解决方案。
【解决方案3】:

你可以简单地返回最右边的向量的结束迭代器,这是合乎逻辑的:

auto find_ten=[](const std::vector<int>& v1,const std::vector<int>& v2){
    auto it1=std::find(v1.cbegin(),v1.cend(),10);
    if(it1==v1.cend()){
        return std::find(v2.cbegin(),v2.cend(),10);
    }
    return it1;
}

auto it = find_ten(v1, v2);
if (it == v2.end()) // no luck

或者您可以采用迭代器参数:

auto find_ten=[](const std::vector<int>& v1,const std::vector<int>& v2, std::vector<int>::iterator endit){
    auto it1=std::find(v1.cbegin(),v1.cend(),10);
    if(it1==v1.cend()){
        auto it2=std::find(v2.cbegin(),v2.cend(),10);
        if(it2==v2.cend()){
             return endit;
        }
        return it2;
    }
    return it1;
}

当然,这两者都取决于您的用例:如果您要使用返回的迭代器来不仅仅是直接访问元素,则需要考虑不同的方法:

--- 编辑 ---

如果您需要使用迭代器而不只是直接访问有问题的元素,您可以使用std::reference_wrapper 让您返回对有问题的向量的引用(或者您可以只返回一个指针)。

#include <vector>
#include <algorithm>
#include <functional>
#include <iostream>

using find_ten_t = std::pair<std::reference_wrapper<const std::vector<int>>, std::vector<int>::const_iterator>;

auto find_ten = [](const std::vector<int>& v1, const std::vector<int>& v2) -> find_ten_t {
    auto it1 = std::find(v1.cbegin(), v1.cend(), 10);
    if (it1 != v1.cend()) {
        return std::make_pair(std::ref(v1), it1);
    }
    auto it2 = std::find(v2.cbegin(), v2.cend(), 10);
    return std::make_pair(std::cref(v2), it2);
};

int main() {
    std::vector<int> v1{ 1, 2, 3 };
    std::vector<int> v2{ 3, 4, 10 };
    auto r = find_ten(v1, v2);
    std::cout << "r.first[0] = " << r.first.get()[0] << "\n";
}

现场演示:http://ideone.com/cNLJKg

【讨论】:

  • @HumamHelfawi 如果您需要的不仅仅是直接访问,请参阅编辑。
【解决方案4】:

我想不是很出色,但是...我建议返回一个std::pair,其中第一个元素是一个 int(0 表示“未找到”,1 表示“在第一个中找到”,2 表示“在第二个中找到” "),第二个是迭代器。

类似

auto find_ten=[](const std::vector<int>& v1,const std::vector<int>& v2){
    auto it1=std::find(v1.cbegin(),v1.cend(),10);
    if(it1==v1.cend()){
        auto it2=std::find(v2.cbegin(),v2.cend(),10);
        if(it2==v2.cend()){
            return std::make_pair(0, v1.cend()); // or make_pair(0, v2.cend())
        }
        return std::make_pair(2, it2);
    }
    return std::make_pair(1, it1);
};

或者更好:你可以返回一对迭代器,其中第二个是对应向量的cend();像

auto find_ten=[](const std::vector<int>& v1,const std::vector<int>& v2){
    auto it1=std::find(v1.cbegin(),v1.cend(),10);
    if(it1==v1.cend()){
       return std::make_pair(std::find(v2.cbegin(),v2.cend(),10), v2.cend());
    }
    return std::make_pair(it1, v1.cend());
};

我认为返回 cend() correspindig 迭代器很重要,因为我想您想使用指向 10 的迭代器并且可以对其进行迭代。

显然,如果您有兴趣了解 v1v2 中是否存在 10,我想您应该返回一个 booltrue 以表示“找到", false 否则;像

auto find_ten=[](const std::vector<int>& v1,const std::vector<int>& v2){
    auto ret = v1.cend() != std::find(v1.cbegin(),v1.cend(),10);
    if ( ret == false )
       ret = v2.cend() != std::find(v2.cbegin(),v2.cend(),10);
    return ret;
};

ps:对不起,我的英语不好。

【讨论】:

  • @Jarod42 - 我想使用 std::option 可以是一个解决方案,但是......可能是我的限制......我不明白在没有有关信息的情况下返回 const-iterator 的用处被引用的容器。
  • @Jarod42 - 没有人是完美的 :-)
猜你喜欢
  • 2017-02-23
  • 2023-04-10
  • 1970-01-01
  • 2011-01-04
  • 2018-05-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-03-01
相关资源
最近更新 更多