使用具有循环依赖性的 ADL 的 C++20 概念

C++20 concepts using ADL with circular dependency

我对使用 ADL 的概念有疑问。

编辑 1:我提到 ADL,因为 parse 函数应该用用户定义的类型重载。

from_string_view_parsable 概念没有看到下面的 parse 函数,因为 ADL 不适用于它们。

函数需要在概念定义之前定义或前向声明,但是第二次重载存在循环依赖,因此无法完成。

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);
}

我尝试做的事情是根本错误的,还是有任何解决方法可以让我这样做?

您可以将 requires 表达式推迟到类型特征,它可以被前向声明:

#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);
}

现在,我不确定这是否违反了任何规则,但我认为不应该。请注意,约束的评估不会在程序的不同点发生变化。

由于无法向前声明概念,因此 from_string_view_parsable 的定义必须出现在 std::optional 重载之后。

相反,您可以使用 requires 子句(或 static_assert)来约束 parse(intput, *out) 必须格式正确,如下所示:

#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);
}