namespace std重载小于

时间:2015-05-08 00:36:03

标签: c++ c++11 tuples std

我很好奇为什么这段代码不起作用:

#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似乎不会影响任何事情。

我找到了三种方法来修复&#34;此

修复#1

环绕布尔运算符&lt; ...在命名空间std中如此:

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<);

这似乎是最优雅的解决方案。但是,我不明白为什么我必须明确告诉编译器使用我定义的运算符&lt;。

所以我想知道:

1)为什么会这样?难道不能找到我的实现并使用它吗?

2)&#34; fix&#34;是最好的?如果我找不到,你会推荐什么?

有什么想法吗?谢谢你的阅读!

1 个答案:

答案 0 :(得分:-1)

你已经自己解决了这个问题

问题在于您的运营商&lt;函数不会覆盖默认的tuple :: operator&lt;,它们位于不同的名称空间

所以,你的Fix#1和Fix#3都是很好的解决方案

修复#1将它们放入相同的命名空间使其覆盖正确,我认为是最好的方法