通过 'tuple' 和 'tie' 实现比较运算符,好主意吗?

Posted

技术标签:

【中文标题】通过 \'tuple\' 和 \'tie\' 实现比较运算符,好主意吗?【英文标题】:Implementing comparison operators via 'tuple' and 'tie', a good idea?通过 'tuple' 和 'tie' 实现比较运算符,好主意吗? 【发布时间】:2011-09-07 07:25:49 【问题描述】:

(注意:tupletie 可以取自 Boost 或 C++11。) 在编写只有两个元素的小型结构时,我有时倾向于选择std::pair,因为该数据类型的所有重要工作都已完成,例如用于严格弱排序的operator<。 缺点是几乎没有用的变量名。即使我自己创建了 typedef,我也不会记得 2 天后 firstsecond 到底是什么,特别是如果它们都是同一类型的话。对于两个以上的成员,情况会变得更糟,因为嵌套 pairs 非常糟糕。 另一个选项是 tuple,来自 Boost 或 C++11,但这看起来并没有更好更清晰。所以我回去自己编写结构,包括任何需要的比较运算符。 因为尤其是 operator< 可能会非常麻烦,所以我想通过仅依靠为 tuple 定义的操作来规避这整个混乱:

operator< 的示例,例如对于严格-弱排序:

bool operator<(MyStruct const& lhs, MyStruct const& rhs)
  return std::tie(lhs.one_member, lhs.another, lhs.yet_more) <
         std::tie(rhs.one_member, rhs.another, rhs.yet_more);

tie 从传递的参数中生成 tupleT&amp; 引用。)


编辑:@DeadMG 提出的从tuple 私下继承的建议不错,但也有不少缺点:

如果运营商是独立的(可能是朋友),我需要公开继承 通过强制转换,可以轻松绕过我的函数/运算符(特别是 operator=) 使用 tie 解决方案,如果某些成员对订购无关紧要,我可以忽略它们

我需要考虑此实现中的任何缺点吗?

【问题讨论】:

在我看来完全合理... 这是一个非常聪明的想法,即使它没有成功。我将不得不对此进行调查。 我喜欢这个主意!如果 tie(...) 调用将在各种运算符(=、==、make_tuple(...) 来封装它,然后从其他各种地方调用它,如return lhs.make_tuple() &lt; rhs.make_tuple();(尽管声明该方法的返回类型可能很有趣!) @aldo:C++14 来救援! auto tied() const return std::tie(the, members, here); 它使它更具可读性和更容易,但一个问题是字符串。这会导致两个运算符被调用 string 吗? string::compare 可用于只进行一次比较,而不是遍历字符串两次。元组的最坏情况可能会遍历字符串两次以检查是否相等。 【参考方案1】:

这肯定会让编写一个正确的运算符比自己滚动它更容易。如果分析显示比较操作是您的应用程序的一个耗时部分,我会说只考虑一种不同的方法。否则,维护它的便利性应该超过任何可能的性能问题。

【讨论】:

我无法想象tuple&lt;&gt;operator&lt;会比手写的慢。 我曾经有过同样的想法,并做了一些实验。看到编译器内联并优化了与元组和引用有关的所有内容,发出的程序集几乎与手写代码相同,这让我感到非常惊讶。 @JohannesD:我可以支持那个证词,做过一次 这是否保证严格的弱排序?怎么样?【参考方案2】:

我遇到了同样的问题,我的解决方案使用 c++11 可变参数模板。代码来了:

.h 部分:

/***
 * Generic lexicographical less than comparator written with variadic templates
 * Usage:
 *   pass a list of arguments with the same type pair-wise, for intance
 *   lexiLessthan(3, 4, true, false, "hello", "world");
 */
bool lexiLessthan();

template<typename T, typename... Args>
bool lexiLessthan(const T &first, const T &second, Args... rest)

  if (first != second)
  
    return first < second;
  
  else
  
    return lexiLessthan(rest...);
  

以及不带参数的基本情况的 .cpp:

bool lexiLessthan()

  return false;

现在你的例子变成了:

return lexiLessthan(
    lhs.one_member, rhs.one_member, 
    lhs.another, rhs.another, 
    lhs.yet_more, rhs.yet_more
);

【讨论】:

我在这里提出了类似的解决方案,但不需要 != 运算符。 ***.com/questions/11312448/…【参考方案3】:

在我看来,您仍然没有解决 std::tuple 解决的相同问题 - 即,您必须知道每个成员变量的数量和名称,您在函数中复制了两次。您可以选择private 继承。

struct somestruct : private std::tuple<...> 
    T& GetSomeVariable()  ... 
    // etc
;

这种方法一开始有点有点混乱,但你只是在一个地方维护变量和名称,而不是在每个地方为你希望重载的每个运算符维护.

【讨论】:

所以我会使用命名访问器来访问 T&amp; one_member() return std::get&lt;0&gt;(*this); 等变量吗?但这不需要我为我拥有的每个“成员”提供这样的方法吗,包括 const 和非 const 版本的重载? @Xeo 我不认为命名访问器需要比创建实际变量更多的工作。无论哪种方式,您都必须为每个变量指定一个单独的名称。我想const/non-const会有重复。但是,您可以对所有这些工作进行模板化。【参考方案4】:

如果您打算使用多个运算符重载或元组中的多个方法,我建议将元组设为类的成员或从元组派生。否则,您正在做的工作更多。在两者之间做出决定时,需要回答的一个重要问题是:您希望您的类成为一个元组吗?如果不是,我建议包含一个元组并使用委托来限制接口。

您可以创建访问器来“重命名”元组的成员。

【讨论】:

我将 OP 的问题理解为“使用 std::tie 实现我的课程 operator&lt; 合理吗?”我不明白这个答案与那个问题有什么关系。 @ildjarn 我没有在这里发布一些 cmets。我已经编译了所有内容,以便更好地阅读。

以上是关于通过 'tuple' 和 'tie' 实现比较运算符,好主意吗?的主要内容,如果未能解决你的问题,请参考以下文章

当列包含`List`而不是`Tuple`时,熊猫比较运算符`==`无法按预期工作

C++标准库 -- tuple

C++中tuple的使用

python数据类型和运算符及运算符的优先级

PythonStudy——元组 Tuple

python基础-元组(tuple)及内置方法