The following fails to compile.
template <typename... U>
requires requires(U... x) { (std::to_string(x) && ...); }
auto to_string(const std::variant<U...>& value) {
return std::visit([](auto&& value) {
return std::to_string(std::forward<decltype(value)>(value));
}, value);
}
int main() {
std::variant<int, float> v = 42;
std::cout << to_string(v) << std::endl;
}
https://godbolt.org/z/Wvn6E3PG5
If I convert the direct requires expression into a concept though, it works fine.
template<typename T, typename... U>
concept to_stringable = requires(U... u) { (std::to_string(u) && ...); };
template <to_stringable... U>
auto to_string(const std::variant<U...>& value) {
return std::visit([](auto&& value) {
return std::to_string(std::forward<decltype(value)>(value));
}, value);
}
int main() {
std::variant<int, float> v = 42;
std::cout << to_string(v) << std::endl;
}
When you have:
template <to_stringable... U>
auto to_string(const std::variant<U0, U1>& value) {
This checks if each individual type satisfies to_stringable<T>
, so it is essentially equivalent to:
template <to_stringable U0, to_stringable U1>
auto to_string(const std::variant<U0, U1>& value) {
And with just one argument T
your concept is:
requires(T t) { (std::to_string(t)); };
However, with more than one argument, it looks like:
requires(T1 t1, T2 t2) { (std::to_string(t1) && std::to_string(t2)); }
Which doesn't work because you can't &&
two std::string
s, so the constraint is not satisfied.
What you really want to do is fold over a constraint:
template <typename... U>
requires ((requires(U x) { std::to_string(x); }) && ...)
... Which gcc doesn't seem to implement properly because of a bug but you can get away with:
template <typename... U>
requires requires(U... x) { ((void) std::to_string(x), ...); }