std::set迭代器的返回类型冲突

4
今天我遇到了第二个编译器错误,让我感到困惑。对于下面的代码,gcc 抱怨代码具有返回迭代器 return_iter 的函数返回冲突类型 std::_Rb_tree_iterator<const int*>std::_Rb_tree_const_iterator<const int*>,但它们都不应该是 const 迭代器,因为 set 不是 const。有人能解释一下为什么在非 const lvalue 上调用 std::end() 方法会返回一个 const_iterator 吗?
完整的代码如下。 注意:只有当使用 gcc 编译时,才会出现此错误。当我使用 clang(Apple LLVM version 8.0.0 (clang-800.0.38))编译时,不会出现此错误。我正在使用的 gcc 版本是 g++ (GCC) 5.1.0
相关问题。这是 forward 的正确使用方法吗?每当您想使用转发引用时,调用 std::forward 是否被认为是可以接受的?我之所以在下面调用它,是为了防止类型重载某些方法,以便在对象是 rvalue 时使用。
#include <vector>
#include <string>
#include <set>
#include <iostream>
using namespace std;

int global_value = 1;

class LessPtr {
public:

    template <typename PointerComparableOne, typename PointerComparableTwo>
    constexpr auto operator()(PointerComparableOne&& lhs,
                              PointerComparableTwo&& rhs) const {
        return *std::forward<PointerComparableOne>(lhs) <
            *std::forward<PointerComparableTwo>(rhs);
    }

    using is_transparent = std::less<void>::is_transparent;
};

template <typename Container, typename Key>
auto return_iter(Container&& container, Key&& key) {
    if (global_value == 1) {
        return std::forward<Container>(container).lower_bound(std::forward<Key>(key));
    }
    else {
        return std::end(std::forward<Container>(container));
    }
}

void do_stuff(std::set<const int*, LessPtr>& set_ptrs) {
    // auto value = string{"something"};
    auto value = 1;
    auto iter = return_iter(set_ptrs, &value);
    cout << reinterpret_cast<void*>(&iter) << endl;
}

int main() {
    std::set<const int*, LessPtr> set_ptrs;
    do_stuff(set_ptrs);

    return 0;
}
< p > LessPtr 在某种程度上是导致此错误的原因。


顺便说一句,不要执行std::end(std::forward<Container>(container)) - T.C.
@T.C. 我正在寻找有人对此发表评论!你能解释一下为什么这样做不好吗?我在思考了一段时间后,无法得出一个好的结论,为什么我不应该这样做。 - Curious
beginend是不适用于rvalue的,如果给定一个rvalue,则它们的行为不正确。 - T.C.
1个回答

5
这是一个有关it技术的问题,涉及到std::set中透明比较器的处理方式存在libstdc++ bug。以下是一个简短的示例:
int main() {
    using S = std::set<const int*, LessPtr>;
    S set_ptrs;

    int i = 0;
    const int ci = 0;
    static_assert(std::is_same<
          decltype(set_ptrs.lower_bound(&ci)), S::iterator>{}, "!"); // OK
    static_assert(std::is_same<
          decltype(set_ptrs.lower_bound(&i)), S::iterator>{}, "!");  // Error

    return 0;
}

第一个断言是正确的,我们调用了lower_bound(Key const&),它返回一个iterator。第二个断言失败是因为我们正在调用函数模板template<class K> lower_bound(K const&),因为LessPtr是透明的,该重载是更好的匹配项(因为它是精确匹配),对于libstdc++,它返回一个const_iterator。但是set_ptrs不是const,所以它不应该是这样的。我已经提交了78134

谢谢你的回答!a_tran 究竟是什么? - Curious
是的,这绝对是gcc实现中的一个错误。在clang实现中,似乎对两种情况都正确地返回了const_iterator - Curious
@Curious 是的,已经更新了。在两种情况下都应该返回“iterator”,而不是“const_iterator”。你的“set”不是“const”。 - Barry
@Barry如果你可以修改你的答案以反映具有透明比较器的lower_bound返回一个“迭代器”而不是“const_iterator”的事实,并且std :: end()返回一个“迭代器”,我会接受这个答案! - Curious
它应该返回一个const_iterator,在我使用的clang实现中,所有<set>的方法都至少返回一个const_iterator。因为它们应该是这样的,对吧? - Curious
显示剩余11条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接