为什么 std::visit 在一个不满足的概念中导致 gcc 编译错误
Why does std::visit in an unsatisfied concept cause a compile error in gcc
此代码:
#include <concepts>
#include <string>
#include <variant>
struct any_callable {
public:
template<typename T>
void operator()(T&&) {}
};
template<typename V>
concept is_variant = requires(V v) { std::visit(any_callable{}, v); };
int main() {
constexpr bool wrapped = is_variant<std::string>;
}
不能在 gcc 11 下编译。它给出了一堆关于 valueless_by_exception
之类的错误。但是,它确实在 msvc (godbolt) 下编译。现在,据我了解概念,在这种情况下,如果它通常无法编译,它将衰减为 false,否则为 true。 msvc 的行为似乎支持这一点。
所以:这是 gcc 中的错误吗,这是 msvc 的一个非标准功能,and/or 是我的代码有误吗?
就是这个:
template<typename V>
concept is_variant = requires(V v) { std::visit(any_callable{}, v); };
完全有效是最近的更改,是 P2162 的结果。为了使此检查生效,您需要 std::visit
成为通常所说的“SFINAE 友好”。也就是说:它必须以某种方式限制“变体”,这样如果 V
是一个变体,它就可以工作,如果 V
不是一个变体,那么 visit
从重载设置使得此调用是无效表达式(因此 concept
可以被拒绝)。
但是,在 P2162 之前,std::visit
上没有 约束。这不是 SFINAE 友好的:调用要么有效要么格式不正确。正是这篇论文 添加了 您传递给它的类型是变体(或从一个变体继承)的约束。这就是为什么您会看到您看到的错误:对 visit
的调用失败了,但不是以一种对 concept
检查友好的方式。
Post-P2162(正如论文指出的那样,MSVC 已经实现,但 libstdc++ 没有),您的检查将有效。
但我们可以在 C++20 中更轻松地做到这一点,而不必进入变体机制——通过以与论文相同的方式直接检查变体:
template<typename V>
concept is_variant = requires(V v) {
[]<typename... Ts>(std::variant<Ts...> const&){}(v);
};
如果 v
是 variant
或继承自 variant
,则 lambda 可通过 v
调用。否则,它的格式不正确。这比通过 visit
.
更直接
此代码:
#include <concepts>
#include <string>
#include <variant>
struct any_callable {
public:
template<typename T>
void operator()(T&&) {}
};
template<typename V>
concept is_variant = requires(V v) { std::visit(any_callable{}, v); };
int main() {
constexpr bool wrapped = is_variant<std::string>;
}
不能在 gcc 11 下编译。它给出了一堆关于 valueless_by_exception
之类的错误。但是,它确实在 msvc (godbolt) 下编译。现在,据我了解概念,在这种情况下,如果它通常无法编译,它将衰减为 false,否则为 true。 msvc 的行为似乎支持这一点。
所以:这是 gcc 中的错误吗,这是 msvc 的一个非标准功能,and/or 是我的代码有误吗?
就是这个:
template<typename V>
concept is_variant = requires(V v) { std::visit(any_callable{}, v); };
完全有效是最近的更改,是 P2162 的结果。为了使此检查生效,您需要 std::visit
成为通常所说的“SFINAE 友好”。也就是说:它必须以某种方式限制“变体”,这样如果 V
是一个变体,它就可以工作,如果 V
不是一个变体,那么 visit
从重载设置使得此调用是无效表达式(因此 concept
可以被拒绝)。
但是,在 P2162 之前,std::visit
上没有 约束。这不是 SFINAE 友好的:调用要么有效要么格式不正确。正是这篇论文 添加了 您传递给它的类型是变体(或从一个变体继承)的约束。这就是为什么您会看到您看到的错误:对 visit
的调用失败了,但不是以一种对 concept
检查友好的方式。
Post-P2162(正如论文指出的那样,MSVC 已经实现,但 libstdc++ 没有),您的检查将有效。
但我们可以在 C++20 中更轻松地做到这一点,而不必进入变体机制——通过以与论文相同的方式直接检查变体:
template<typename V>
concept is_variant = requires(V v) {
[]<typename... Ts>(std::variant<Ts...> const&){}(v);
};
如果 v
是 variant
或继承自 variant
,则 lambda 可通过 v
调用。否则,它的格式不正确。这比通过 visit
.