【问题标题】:namespace std overloading less than命名空间标准重载小于
【发布时间】: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&lt;是更好的匹配,因为它的参数是const
  • @Oktalist 哎呀,忘了提,但是在两个参数之前添加 const 没有帮助。将编辑
  • 问题是因为已经有一个operator&lt; 用于元组,所以编译器使用的是官方的而不是你的hack。

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


【解决方案1】:

您的operator&lt; 重载在使用&lt; 的位置不可见(在std::sort 和/或它调用的任何辅助函数的主体中,在&lt;algorithm&gt; 的某处)。

如果要使用,必须通过argument-dependent lookup来获取;但是std::tuple&lt;int, std::string&gt; 中没有任何内容将全局命名空间作为关联的命名空间,因此 ADL 也对您没有帮助,并且使用了标准的。

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

【讨论】:

  • 好吧,我想是这样的。有没有办法“强制”ADL 来接它?可能类似于 using namespace global (尝试过 namespace global 并没有用)
  • @BillyWon 不是不改变类型。我也真的不建议编写与标准库提供的不同的 &lt;
  • 谢谢!将更改为更具描述性的名称。让它重载
【解决方案2】:

你已经自己修好了

问题是您的 operator

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

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

【讨论】:

  • 并在过程中导致未定义的行为。是的,这绝对是最好的方法。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-08-29
  • 1970-01-01
  • 2023-03-26
  • 2013-02-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多