Consider the following concept
which requires
the value_type
of a range
R
is printable:
#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; };
It works fine with std::vector<int>
on different three compliers:
static_assert(printable_range<std::vector<int>>);
but if I define a template operator<<
function with any type x
after concepts
define:
std::ostream& operator<<(std::ostream& os, const auto& x) { return os << x; }
GCC and MSVC can pass the following assert but Clang fails:
static_assert(printable_range<std::vector<std::vector<int>>>);
Which compiler should I trust? It seems like a Clang bug.
Weirdly, If I define a custom struct S
with operator<<
support before the concept
printable_range
define:
struct S{};
std::ostream& operator<<(std::ostream& os, const S&) { return os; }
Same assert fails with MSVC, but GCC still accept it:
static_assert(printable_range<std::vector<std::vector<int>>>);
Is it an MSVC bug?
If I transform the operator<<
function into a named function print
, then all the compiler fails on the second assert. This surprised me since it looks equivalent to case 1, the key points here are the member function vs. free function or operator overloading function vs. free function?
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!
Which compiler should I trust? It seems like a Clang bug.
This is a GCC/MSVC bug. Name lookup for os << x
will perform argument-dependent lookup to find any other associated operator<<
s, but the associated namespaces here are just std
. Your operator<<
is not in namespace std
, so lookup should not find it, so there should be no viable candidates.
The fact that GCC and MSVC do so is a bug.
The issue with GCC is that its lookup with operators, specifically, just finds more things than it should (see 51577, thanks T.C.). That's why it can find the operator<<
but not the print
.
Really, these are the same example, just with a different name (print
vs operator<<
) and they should have the same behavior.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With