通过 'tuple' 和 'tie' 实现比较运算符,好主意吗?
Posted
技术标签:
【中文标题】通过 \'tuple\' 和 \'tie\' 实现比较运算符,好主意吗?【英文标题】:Implementing comparison operators via 'tuple' and 'tie', a good idea?通过 'tuple' 和 'tie' 实现比较运算符,好主意吗? 【发布时间】:2011-09-07 07:25:49 【问题描述】:(注意:tuple
和 tie
可以取自 Boost 或 C++11。)
在编写只有两个元素的小型结构时,我有时倾向于选择std::pair
,因为该数据类型的所有重要工作都已完成,例如用于严格弱排序的operator<
。
缺点是几乎没有用的变量名。即使我自己创建了 typedef
,我也不会记得 2 天后 first
和 second
到底是什么,特别是如果它们都是同一类型的话。对于两个以上的成员,情况会变得更糟,因为嵌套 pair
s 非常糟糕。
另一个选项是 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
从传递的参数中生成 tuple
的 T&
引用。)
编辑:@DeadMG 提出的从tuple
私下继承的建议不错,但也有不少缺点:
operator=
)
使用 tie
解决方案,如果某些成员对订购无关紧要,我可以忽略它们
我需要考虑此实现中的任何缺点吗?
【问题讨论】:
在我看来完全合理... 这是一个非常聪明的想法,即使它没有成功。我将不得不对此进行调查。 我喜欢这个主意!如果tie(...)
调用将在各种运算符(=、==、make_tuple(...) 来封装它,然后从其他各种地方调用它,如return lhs.make_tuple() < rhs.make_tuple();
(尽管声明该方法的返回类型可能很有趣!)
@aldo:C++14 来救援! auto tied() const return std::tie(the, members, here);
它使它更具可读性和更容易,但一个问题是字符串。这会导致两个运算符被调用 string 吗? string::compare 可用于只进行一次比较,而不是遍历字符串两次。元组的最坏情况可能会遍历字符串两次以检查是否相等。
【参考方案1】:
这肯定会让编写一个正确的运算符比自己滚动它更容易。如果分析显示比较操作是您的应用程序的一个耗时部分,我会说只考虑一种不同的方法。否则,维护它的便利性应该超过任何可能的性能问题。
【讨论】:
我无法想象tuple<>
的operator<
会比手写的慢。
我曾经有过同样的想法,并做了一些实验。看到编译器内联并优化了与元组和引用有关的所有内容,发出的程序集几乎与手写代码相同,这让我感到非常惊讶。
@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& one_member() return std::get<0>(*this);
等变量吗?但这不需要我为我拥有的每个“成员”提供这样的方法吗,包括 const 和非 const 版本的重载?
@Xeo 我不认为命名访问器需要比创建实际变量更多的工作。无论哪种方式,您都必须为每个变量指定一个单独的名称。我想const/non-const会有重复。但是,您可以对所有这些工作进行模板化。【参考方案4】:
如果您打算使用多个运算符重载或元组中的多个方法,我建议将元组设为类的成员或从元组派生。否则,您正在做的工作更多。在两者之间做出决定时,需要回答的一个重要问题是:您希望您的类成为一个元组吗?如果不是,我建议包含一个元组并使用委托来限制接口。
您可以创建访问器来“重命名”元组的成员。
【讨论】:
我将 OP 的问题理解为“使用std::tie
实现我的课程 operator<
合理吗?”我不明白这个答案与那个问题有什么关系。
@ildjarn 我没有在这里发布一些 cmets。我已经编译了所有内容,以便更好地阅读。以上是关于通过 'tuple' 和 'tie' 实现比较运算符,好主意吗?的主要内容,如果未能解决你的问题,请参考以下文章