如何解决在 msvc 和 clang 中导致编译器错误的概念?
我有以下代码:
#include <variant>
template <typename T>
struct S{};
using Var = std::variant<S<int>,S<float>>;
template <typename T>
concept VariantMember = requires(Var var) { std::get<T>(var); };
void foo(VariantMember auto x) {}
void foo(auto x) {}
void bar()
{
foo(S<int>{});
foo(S<char>{});
}
MSVC 错误
错误 C2338:get(variant
在这种情况下,仅当我在变体 S
中使用同一模板的两个实例时才会发生这种情况。所以首先 std::get
意外错误,然后概念不会失败,而是直接错误。有趣的是,如果我在概念之外使用 std::get
,例如:
std::get<S<int>>(Var{ S<int>{} });
代码编译得很好。
Clang 似乎忽略了这个概念,并且在这两种情况下总是选择 foo
的第一个重载。
那些真的是错误还是我搞砸了?
解决方法
首先,您的 S
模板是噪音。删除它。
using Var = std::variant<int,float>;
template <typename T>
concept VariantMember = requires(Var var) { std::get<T>(var); };
template <int = 1>
void foo(VariantMember auto x) {}
template <int = 2>
void foo(auto x) {}
void bar()
{
foo(int{});
foo(char{});
}
我们得到同样的错误。如果我注释掉 char
情况,它在 MSVC 中编译得很好。
std::get<char>(Var{})
不需要对 SFINAE 友好。 std::get
可以(并且确实)在 MSVC 中硬失败。
在这个版本中,一旦我删除了导致严重错误的 int=1
重载,clang 和 msvc 都会调用 char
重载。
我们如何处理硬错误?写一个没有的 get
; safe_get
:
template<class T,class...Ts>
requires (1 == (std::is_same_v<T,Ts>+...))
T& safe_get( std::variant<Ts...>& var )
return std::get<T>(var);
}
template<class T,Ts>+...))
T const& safe_get( std::variant<Ts...> const& var )
return std::get<T>(var);
}
// ...
template <typename T>
concept VariantMember = requires(Var var) {
{safe_get<T>(var)};
};
(我添加并声明 1,这给了我“匹配且仅一次”)。
然后我们返回您的 S
模板和 everything works。
Yakk 解释了这个问题:std::get
mandates 类型出现在变体中。它不是 SFINAE 友好的(即它不是约束)。因此,您的 VariantMember<T>
概念并没有真正检查任何内容:它要么是 true
,要么是格式错误的。
相反,我们必须以不同的方式编写概念。使用 Boost.Mp11,这是一个简短的单行(一如既往):
template <typename T>
concept VariantMember = mp_contains<Var,T>::value;
我不清楚您是否需要检查 T
是否只是变体的成员(如我在上面实现的),或者您是否需要检查 T
是否在变体。如果是后者,那只是不同的算法:
template <typename T>
concept VariantMember = mp_count<Var,T>::value == 1;
,
与 Yakk 类似,我想出了自己的 get
。我首先使用 get
的原因是为了找出类型 T
是否是 std::variant
的成员。所以我想我只是测试我是否可以使用 std::get
对变体调用 T
。
无论如何,这是我的解决方案,以防有人发现它有用:
namespace detail
{
template <typename Test,typename Variant>
struct CountVariantMembers;
template <typename Test,template <typename ...> typename Variant,typename ...Args>
struct CountVariantMembers<Test,Variant<Args...>>
{
static constexpr std::size_t Count = (std::same_as<Test,Args> + ...);
};
}
template <typename T,typename Variant>
concept VariantMember = details::CountVariantMembers<T,Variant>::Count > 0;
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。