【发布时间】:2015-05-08 00:36:03
【问题描述】:
我很好奇为什么这段代码不起作用:
#include "stdafx.h"
#include <iostream>
#include <tuple>
#include <string>
#include <vector>
#include <algorithm>
typedef std::tuple<int, std::string> intString;
bool operator<(intString& lhs, intString& rhs){
return std::tie(std::get<1>(lhs), std::get<0>(lhs)) < std::tie(std::get<1>(rhs), std::get<0>(rhs));
}
void printIntStrings(std::vector<intString>& v){
for (intString& i : v){
std::cout << std::get<0>(i) << " is " << std::get<1>(i) << std::endl;
}
}
int main(int argc, char* argv[])
{
std::vector<intString> v;
v.push_back(std::make_tuple(5, "five"));
v.push_back(std::make_tuple(2, "two"));
v.push_back(std::make_tuple(9, "nine"));
printIntStrings(v);
std::sort(v.begin(), v.end());
printIntStrings(v);
return 0;
}
据我所知,我只是创建了一个 intStrings 向量,我的运算符应该首先按元组中的第二个元素排序,因此输出应该是(无论如何都是最后 3 行)
5 five
9 nine
2 two
但是在我的机器上运行它我得到了
2 two
5 five
9 nine
这意味着排序使用默认的小于运算符,忽略我指定的那个。请注意,在参数之前添加 const 似乎没有任何影响。
我找到了三种“解决”这个问题的方法。
修复 #1
环绕布尔运算符
namespace std{
bool operator<(intString& lhs, intString& rhs){
return std::tie(std::get<1>(lhs), std::get<0>(lhs)) < std::tie(std::get<1>(rhs), std::get<0>(rhs));
}
}
但是有人告诉我,我们不应该向 std 命名空间添加东西,因为这种行为是未定义的,所以这个修复似乎是最糟糕的。
修复 #2
像这样在元组中添加一些自定义的东西:
enum class TRASH{DOESNTMATTER};
typedef std::tuple<int, std::string, TRASH> intString;
bool operator<(intString& lhs, intString& rhs){
return std::tie(std::get<1>(lhs), std::get<0>(lhs)) < std::tie(std::get<1>(rhs), std::get<0>(rhs));
}
(显然添加 TRASH::DOESNTMATTER 作为第三个 make_tuple 参数) 然而,对于这么简单的事情,这似乎需要做很多工作。此外,这似乎很浪费,因为枚举没有被有意义地使用。
修复 #3
像这样使用谓词排序:
std::sort(v.begin(), v.end(), operator<);
这似乎是最优雅的解决方案。但是,我不明白为什么我必须明确告诉编译器使用我定义的运算符<.>
所以我想知道:
1) 为什么会这样? c++ 不应该找到我的实现并使用它吗?
2) 哪个“修复”是最好的?如果我没有找到,你会推荐什么?
有什么想法吗?感谢阅读!
【问题讨论】:
-
标准的
operator<是更好的匹配,因为它的参数是const。 -
@Oktalist 哎呀,忘了提,但是在两个参数之前添加 const 没有帮助。将编辑
-
问题是因为已经有一个
operator<用于元组,所以编译器使用的是官方的而不是你的hack。