命名空间标准重载小于

namespace std overloading less than

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

#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;
}

据我所知,我只是创建了一个 intString 向量,我的运算符应该首先按元组中的第二个元素排序,因此输出应该是(最后 3 行)

5 five
9 nine
2 two

但是 运行 它在我的机器上得到

2 two
5 five
9 nine

这意味着排序使用默认的小于运算符,忽略我指定的那个。请注意,在参数前添加 const 似乎没有任何影响。

我找到了三种方法 "fix" 这个。

修复#1

surround bool operator< ... 在命名空间 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<);

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

所以我想知道:

1) 为什么会这样? c++ 不应该找到我的实现并使用它吗?

2) 哪个 "fix" 最好?如果我找到 none 个,你会推荐什么?

有什么想法吗?感谢阅读!

你已经自己修好了

问题是你的运算符<函数没有覆盖默认的tuple::operator<,它们在不同的命名空间

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

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

你的 operator< 重载在使用 < 的地方是不可见的(在 std::sort and/or 它调用的任何辅助函数的主体中,在 <algorithm>) 的某处。

如果要使用,必须通过argument-dependent lookup来获取;但是 std::tuple<int, std::string> 中没有任何内容将全局命名空间作为关联的命名空间,因此 ADL 也无济于事,使用的是标准命名空间。

将其作为比较器传递,最好使用 lambda 或函数对象(内联比函数指针更好),这是最简单的解决方法。我还建议重命名它;具有与标准语义完全不同的语义的 operator< 重载,表达式 a < b 可能会或可能不会使用它取决于该表达式的位置,这不是一个好主意。