简体   繁体   English

对 reverse_iterator 的 C++20 更改会破坏此代码吗?

[英]What C++20 change to reverse_iterator is breaking this code?

The following code compiles in C++11, C++14, and C++17, but does not compile in C++20.以下代码在 C++11、C++14 和 C++17 中编译,但在 C++20 中不编译。 What change to the standard broke this code?对标准的哪些更改破坏了此代码?

#include <vector>
#include <utility>

template<typename T>
struct bar
{
    typename T::reverse_iterator x;
};

struct foo
{
    bar<std::vector<std::pair<foo, foo>*>> x;
};

int main()
{
    foo f;
}

The error is quite long, but can be summarized as:错误很长,但可以总结为:

template argument must be a complete class模板参数必须是完整的 class

This was always undefined.这始终是未定义的。 [res.on.functions]/2.5 says: [res.on.functions]/2.5说:

In particular, the effects are undefined in the following cases:特别是,在以下情况下效果是不确定的:

  • [...] [...]
  • If an incomplete type ([basic.types]) is used as a template argument when instantiating a template component or evaluating a concept, unless specifically allowed for that component.如果在实例化模板组件或评估概念时将不完整的类型 ([basic.types]) 用作模板参数,除非该组件特别允许。

std::pair does not (and cannot) support incomplete types. std::pair不(也不能)支持不完整的类型。 You were just relying on order of instantiation to kind of get around that.你只是依靠实例化的顺序来解决这个问题。 Something changed in the library that slightly changed the evaluation order, leading to the error.库中的某些更改略微更改了评估顺序,从而导致错误。 But undefined behavior is undefined - it happened to work before and it happens to not work now.但是未定义的行为是未定义的——它以前碰巧起作用,现在碰巧不起作用。


As to why it's specifically C++20 that is causing this to fail.至于为什么它特别是 C++20 导致它失败。 In C++20, iterators changed to have this new iterator_concept idea.在 C++20 中,迭代器更改为具有这个新的iterator_concept理念。 In order to instantiate that, reverse_iterator needs to determine what the concept should be.为了实例化, reverse_iterator需要确定概念应该是什么。 That looks like this :看起来像这样

#if __cplusplus > 201703L && __cpp_lib_concepts
      using iterator_concept
    = conditional_t<random_access_iterator<_Iterator>,
            random_access_iterator_tag,
            bidirectional_iterator_tag>;
      using iterator_category
    = __detail::__clamp_iter_cat<typename __traits_type::iterator_category,
                     random_access_iterator_tag>;
#endif

Now, in the process of checking random_access_iterator , the root of iterator concept hierarchy is wonderfully named input_or_output_iterator , specified in [iterator.concept.iterator] :现在,在检查random_access_iterator的过程中,迭代器概念层次结构的根奇妙地命名为input_or_output_iterator ,在[iterator.concept.iterator]中指定:

template<class I>
  concept input_or_output_iterator =
    requires(I i) {
      { *i } -> can-reference;
    } &&
    weakly_incrementable<I>;

So, we have to do *i on our iterator type.所以,我们必须对我们的迭代器类型做*i That's __gnu_cxx::__normal_iterator<std::pair<foo, foo>**, std::vector<std::pair<foo, foo>*> > , in this case.这是__gnu_cxx::__normal_iterator<std::pair<foo, foo>**, std::vector<std::pair<foo, foo>*> > ,在这种情况下。 Now, *i triggers ADL - because of course it does.现在, *i触发 ADL - 因为它当然会触发。 And ADL requires instantiation of all the associated types - because those associated types might have injected friends that could be candidates!并且 ADL 需要实例化所有关联类型——因为这些关联类型可能已经注入了可能成为候选对象的朋友!

This, in turn, requires instantiating pair<foo, foo> - because, we have to check.反过来,这需要实例化pair<foo, foo> - 因为,我们必须检查。 And then that ultimately fails in this specific case because instantiating a type requires instantiating all of the type's special member functions, and the way that libstdc++ implements conditional assignment for std::pair is using Eric Fisellier's trick :然后在这种特定情况下最终失败,因为实例化一个类型需要实例化该类型的所有特殊成员函数,而 libstdc++ 为std::pair实现条件赋值的方式是使用Eric Fisellier 的技巧

      _GLIBCXX20_CONSTEXPR pair&
      operator=(typename conditional<
        __and_<is_copy_assignable<_T1>,
               is_copy_assignable<_T2>>::value,
        const pair&, const __nonesuch&>::type __p)
      {
    first = __p.first;
    second = __p.second;
    return *this;
      }

And is_copy_assignable requires complete types and we don't have one. is_copy_assignable需要完整的类型,而我们没有。

But really even if pair used concepts to check in this case, that would still involve instantiating the same type traits, so we'd ultimately end up in the same position.但实际上,即使在这种情况下, pair使用概念来检查,仍然需要实例化相同的类型特征,所以我们最终会得到相同的 position。

Moral of the story is, undefined behavior is undefined.故事的寓意是,未定义的行为是未定义的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM