微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

CRTP-ed std::vectors

如何解决CRTP-ed std::vectors

this article 中,我偶然发现了这段晦涩的代码(随意呈现,就像这是一个完全正常的 C++ 代码):

struct Tree : std::vector<Tree> {};

然后创建并比较两棵树(参见the demo):

Tree tree(size_t h)
{
  Tree s;
  if (h > 0)
    s.insert(s.end(),2,tree(h - 1));
  return s;
}

int main()
{
  size_t h = 15;
  Tree s1 = tree(h);
  Tree s2 = tree(h);
  clock_t t = clock();
  s1 < s2;
  double d = clock() - t;
  d /= CLOCKS_PER_SEC;
  std::cout << d << std::endl; // 4.1s
}

经过一番思考后,我意识到这种比较似乎是合法的,并且总是会产生错误

文章的思路是,由于C++17用std::lexicographical_compare实现了向量的比较,将两个序列中对应的元素ab比较为{{1} } 和 a<b(参见 cpppreference 的“可能的实现”部分),像上面的 b<a 这样的深层结构的性能将在深度上呈二次方下降。确实如此。

文章也说三路比较没有这样的问题。但是等等,这正是 C++20 中出现的 Tree。但这里有一个警告。

代码无法在 C++20 中编译:

operator<=>

这是我的问题。这个编译错误是编译器中的一个错误,还是这段代码实际上格式错误?如果代码格式错误,是仅适用于 C++20 还是适用于 C++17 和 C++20? (后者意味着此代码只能在 C++17 下偶然编译)。

解决方法

这里的问题是约束递归。

在 C++17 中,vector<T>operator< 有一个先决条件,即 < 是为 T 定义的,但 libstdc++ 没有检查这一点。他们的实现was basically

template <typename T>
inline bool operator<(vector<T> const& a,vector<T> const& b) {
    return lexicographical_compare(a.begin(),a.end(),b.begin(),b.end());
}

这...只是有效。

但在 C++20 中,vector<T> 改为 operator<=>,它有一个约束:

template <typename T>
    // this checks either <=> or <
    requires synth_3way_comparable<T>
auto operator<=>(vector<T> const& a,vector<T> const& b) { ... }

这里的问题是:为了弄清楚Tree是三路可比的,我们必须弄清楚我们是否可以在两个<=>上调用Tree,其中找到这个 operator<=>(vector<Tree>,vector<Tree>) 候选,它只在 Tree 是三路可比的时候才定义,所以我们必须弄清楚我们是否可以在两个 <=> 上调用 Tree,哪个发现...

所以...不起作用,也不能工作。

不幸的是,这甚至不是一个问题:只需将 ==<=> 添加到 Tree,因为 vector<=> 仍将是候选人并且只是问它是否有效的问题就爆炸了。

因此,这不是在 C++20 中实现比较的可行策略。您将无法像这样继承 vector。您只需将其添加为成员即可。

,

此代码在 C++17 中格式良好,在 C++20 中格式错误。

考虑以下两个 2 高的树:

struct Tree : std::vector<Tree> {};

int main() {
  Tree s1 = tree(1);
  Tree s2 = tree(1);
  s1 < s2;
}

在 C++17 中,我们只调用 vectoroperator<

bool operator<(const vector<Tree>& x,const vector<Tree>& y) { 
  return std::lexicographical_compare(x.begin(),x.end(),y.begin(),y.end()); 
}

这将调用 std::lexicographical_compare

template <class InputIt1,class InputIt2>
bool lexicographical_compare(InputIt1 first1,InputIt1 last1,InputIt2 first2,InputIt2 last2) {
  for (; (first1 != last1) && (first2 != last2); ++first1,(void)++first2) {
    if (*first1 < *first2) return true;
    if (*first2 < *first1) return false;
  }
  return (first1 == last1) && (first2 != last2);
}

首先比较*first1 < *first2*first1s1的左结点,*first2s2的左结点,它们的类型是{ {1}},因此我们返回来比较两个 Tree。目前,我们没有 Treeoperator<,所以我们回到调用他们基地的 Tree 就像我们第一次做的那样:

operator<

但这一次,两个节点的基数bool operator<(const vector<Tree>& x,y.end()); } 实际上是空的,所以在更深的vector函数中,我们只是返回false。第二个比较 lexicographical_compare 遵循相同的程序。

到此为止,我们完成了左节点比较,然后我们可以*first2 < *first1继续右节点比较。

在 C++20 中,++first1,(void)++first2 重写为:

s1 < s2

所以我们称(s1 <=> s2) < 0 的{​​{1}}:

vector

compare_three_way() 约束 operator<=>auto operator<=>(const vector<Tree>& x,const vector<Tree>& y) { return std::lexicographical_compare_three_way( x.begin(),y.end(),std::compare_three_way() ); } ,即 value_type 必须满足 std::three_way_comparable_with 概念,但我们没有定义任何 {{1} } 为 vector,所以我们得到 Tree

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。