首页 > 解决方案 > C++20 概念需要运算符重载与用户定义的模板运算符重载函数相结合

问题描述

情况1

考虑以下concept哪些a是可打印requires的:value_typerange R

#include <iostream>
#include <iterator>

template <class R, typename T = std::ranges::range_value_t<R>>
concept printable_range = requires(std::ostream& os, const T& x) { os << x; };

它适用std::vector<int>于不同的三个编译器:

static_assert(printable_range<std::vector<int>>);

但是如果我在定义之后定义operator<<了任何类型的模板函数:xconcepts

std::ostream& operator<<(std::ostream& os, const auto& x) { return os << x; }

GCC 和 MSVC 可以通过以下断言但 Clang失败

static_assert(printable_range<std::vector<std::vector<int>>>);

我应该信任哪个编译器?这似乎是一个 Clang 错误。

案例2

奇怪的是,如果我在定义之前定义了一个S支持operator<<的自定义结构concept printable_range

struct S{};
std::ostream& operator<<(std::ostream& os, const S&) { return os; }

MSVC 的相同断言失败,但 GCC 仍然接受它:

static_assert(printable_range<std::vector<std::vector<int>>>);

它是一个 MSVC 错误吗?

案例3

如果我将operator<<函数转换为命名函数print,那么所有编译器都会在第二个断言上失败。这让我感到惊讶,因为它看起来等同于案例 1,这里的关键点是成员函数自由函数运算符重载函数自由函数

void print(int x) { std::cout << x; };

template <class R, typename T = std::ranges::range_value_t<R>>
concept printable_range = requires(const T& x) { print(x); };

void print(auto x) { std::cout << x; };

static_assert(printable_range<std::vector<int>>);
static_assert(printable_range<std::vector<std::vector<int>>>); // failed!

标签: c++templatesoperator-overloadinglanguage-lawyerc++20

解决方案


我应该信任哪个编译器?这似乎是一个 Clang 错误。

这是一个 GCC/MSVC 错误。Name lookup foros << x将执行与参数相关的查找以查找任何其他关联operator<<的 s,但这里关联的命名空间只是std. 你不在,所以查找operator<<不应该找到它,所以应该没有可行的候选人。namespace std

GCC 和 MSVC 这样做的事实是一个错误。

GCC 的问题在于它与操作员的查找,具体来说,只是找到了比它应该的更多的东西(参见51577,感谢 TC)。这就是为什么它可以找到operator<<但不能找到print.

实际上,这些是相同的示例,只是名称不同(printvs operator<<),它们应该具有相同的行为。


推荐阅读