I'm having a problem with concepts using ADL.
edit 1: I mention ADL since the parse
functions are supposed to be overloaded with user defined types.
The from_string_view_parsable
concept doesn't see the parse
functions below since ADL doesn't apply to them.
The functions would need to be defined or forward declared before the concept's definition, however with the 2nd overload there is a circular dependency so it cannot be done.
https://godbolt.org/z/frn1jKv5E
#include <sstream>
#include <optional>
template <typename T>
concept from_string_view_parsable = requires(std::string_view sv, T& x) {
{ parse(sv, x) };
};
void parse(std::string_view input, int& out)
{
auto ss = std::stringstream{};
ss << input;
ss >> out;
}
template <from_string_view_parsable T>
requires std::default_initializable<T>
void parse(std::string_view input, std::optional<T>& out)
{
out = T{};
parse(input, *out);
}
template <from_string_view_parsable T>
void use_parse(T& t) {
parse("123", t);
}
int main() {
std::optional<int> x;
use_parse(x);
}
Is what I'm trying to do fundamentally wrong or perhaps is there any workaround that would allow me to do it?
CodePudding user response:
You can defer the requires
expression to a type trait, which can be forward-declared:
#include <sstream>
#include <optional>
#include <string_view>
#include <type_traits>
template <typename T>
struct is_from_string_view_parsable;
template <typename T>
concept from_string_view_parsable = is_from_string_view_parsable<T>::value;
void parse(std::string_view input, int& out)
{
auto ss = std::stringstream{};
ss << input;
ss >> out;
}
template <from_string_view_parsable T>
requires std::default_initializable<T>
void parse(std::string_view input, std::optional<T>& out)
{
out = T{};
parse(input, *out);
}
template <from_string_view_parsable T>
void use_parse(T& t) {
parse("123", t);
}
template <typename T>
struct is_from_string_view_parsable : std::bool_constant<
requires(std::string_view sv, T& x) {
{ parse(sv, x) };
}
> {};
int main() {
std::optional<int> x;
use_parse(x);
}
Now, I don't know for sure that this doesn't break any rules, but I don't think it should. Be careful that the evaluation of the constraint does not change at different points in the program.
CodePudding user response:
Since concept cannot be forward declared, the definition of from_string_view_parsable
must appear after the std::optional
overload.
Instead, you can use the requires clause (or static_assert
) to constrain parse(intput, *out)
must be well-formed, something like this:
#include <sstream>
#include <optional>
void parse(std::string_view input, int& out)
{
auto ss = std::stringstream{};
ss << input;
ss >> out;
}
template <std::default_initializable T>
void parse(std::string_view input, std::optional<T>& out)
requires requires { parse(input, *out); }
{
out = T{};
parse(input, *out);
}
template <typename T>
concept from_string_view_parsable = requires(std::string_view sv, T& x) {
{ parse(sv, x) };
};
template <from_string_view_parsable T>
void use_parse(T& t) {
parse("123", t);
}
int main() {
std::optional<int> x;
use_parse(x);
}