使类型特性适用于所有派生类型

huangapple go评论88阅读模式
英文:

Making type trait work for all derived types

问题

我有一个类型特性和概念,用于检查std::variant是否能够容纳给定的类型T。现在我有一个类型variant2,它派生自std::variant,我想在新的variant2类型中使用这个类型特性。如何优雅地实现这一目标?

这不起作用(演示):

  1. #include <variant>
  2. #include <string>
  3. #include <iostream>
  4. #include <concepts>
  5. #include <type_traits>
  6. template<typename T, typename Variant>
  7. struct variant_type;
  8. template<typename T, typename... Args>
  9. struct variant_type<T, std::variant<Args...>>
  10. : public std::disjunction<std::is_same<T, Args>...> {};
  11. template<typename T, typename Variant>
  12. concept is_variant_type = variant_type<T, Variant>::value;
  13. template <typename... Ts>
  14. struct variant2 : public std::variant<Ts...> {
  15. };
  16. variant2<std::monostate, int, bool, std::string> var;
  17. int main() {
  18. using T = std::string;
  19. if constexpr (is_variant_type<T, decltype(var)>) {
  20. std::cout << "Worked!" << std::endl;
  21. }
  22. }

"Worked" 从未出现在屏幕上,这显然是因为默认的类型特性被SFINAED。

英文:

I have a type trait and concept which checks if an std::variant can hold a given type T. Now I have a type variant2 which derives from std::variant, and I want to use that type trait with the new variant2 type. How can I accomplish this elegantly?

This doesn't work (Demo):

  1. #include &lt;variant&gt;
  2. #include &lt;string&gt;
  3. #include &lt;iostream&gt;
  4. #include &lt;concepts&gt;
  5. #include &lt;type_traits&gt;
  6. template&lt;typename T, typename Variant&gt;
  7. struct variant_type;
  8. template&lt;typename T, typename... Args&gt;
  9. struct variant_type&lt;T, std::variant&lt;Args...&gt;&gt;
  10. : public std::disjunction&lt;std::is_same&lt;T, Args&gt;...&gt; {};
  11. template&lt;typename T, typename Variant&gt;
  12. concept is_variant_type = variant_type&lt;T, Variant&gt;::value;
  13. template &lt;typename... Ts&gt;
  14. struct variant2 : public std::variant&lt;Ts...&gt; {
  15. };
  16. variant2&lt;std::monostate, int, bool, std::string&gt; var;
  17. int main() {
  18. using T = std::string;
  19. if constexpr (is_variant_type&lt;T, decltype(var)&gt;) {
  20. std::cout &lt;&lt; &quot;Worked!&quot; &lt;&lt; std::endl;
  21. }
  22. }

"Worked" never appears on the screen which is obvious because the default type trait is SFINAED.

答案1

得分: 5

你只需通过表达你的模板必须继承自std::variant来修复你的专业化:

  1. #include <concepts>
  2. #include <iostream>
  3. #include <string>
  4. #include <type_traits>
  5. #include <variant>
  6. template <typename T, typename Variant>
  7. struct variant_type;
  8. template <typename T, template <typename...> typename Var, typename... Args>
  9. struct variant_type<T, Var<Args...>>
  10. : public std::conjunction<
  11. std::disjunction<std::is_same<T, Args>...>,
  12. std::is_base_of<std::variant<Args...>, Var<Args...>>> {};
  13. template <typename T, typename Variant>
  14. concept is_variant_type = variant_type<T, Variant>::value;
  15. template <typename... Ts>
  16. struct variant2 : public std::variant<Ts...> {};
  17. variant2<std::monostate, int, bool, std::string> var;
  18. int main() {
  19. using T = std::string;
  20. if constexpr (is_variant_type<T, decltype(var)>) {
  21. std::cout << "Worked!" << std::endl;
  22. }
  23. }

我只翻译了代码部分,没有包括问题中的附加信息。

英文:

You can just fix your specialization by expressing that your template must inherit from std::variant

  1. #include &lt;concepts&gt;
  2. #include &lt;iostream&gt;
  3. #include &lt;string&gt;
  4. #include &lt;type_traits&gt;
  5. #include &lt;variant&gt;
  6. template &lt;typename T, typename Variant&gt;
  7. struct variant_type;
  8. template &lt;typename T, template &lt;typename...&gt; typename Var, typename... Args&gt;
  9. struct variant_type&lt;T, Var&lt;Args...&gt;&gt;
  10. : public std::conjunction&lt;
  11. std::disjunction&lt;std::is_same&lt;T, Args&gt;...&gt;,
  12. std::is_base_of&lt;std::variant&lt;Args...&gt;, Var&lt;Args...&gt;&gt;&gt; {};
  13. template &lt;typename T, typename Variant&gt;
  14. concept is_variant_type = variant_type&lt;T, Variant&gt;::value;
  15. template &lt;typename... Ts&gt;
  16. struct variant2 : public std::variant&lt;Ts...&gt; {};
  17. variant2&lt;std::monostate, int, bool, std::string&gt; var;
  18. int main() {
  19. using T = std::string;
  20. if constexpr (is_variant_type&lt;T, decltype(var)&gt;) {
  21. std::cout &lt;&lt; &quot;Worked!&quot; &lt;&lt; std::endl;
  22. }
  23. }

I just added a conjunction to achieve that and we'are done.<br>
Live<br>
[EDIT] sorry, it's just a bit more complicated: I added a template template parameter that implies that your inherited class has exactly the same template parameters than the base one, which might be restrictive.

答案2

得分: 2

以下是您要翻译的内容:

由于 variant2 继承了 std::variant,您可以通过以下辅助函数提取其基本的 variant 类型:

  1. template<typename... Args>
  2. auto as_variant(const std::variant<Args...>&) -> std::variant<Args...>;
  3. template<typename T>
  4. struct to_variant;
  5. template<typename T>
  6. requires requires (const T& t) { as_variant(t); }
  7. struct to_variant<T> {
  8. using type = decltype(as_variant(std::declval<const T&>()));
  9. };

然后稍微修改 is_variant_type 概念:

  1. template<typename T, typename Variant>
  2. concept is_variant_type =
  3. variant_type<T, typename to_variant<Variant>::type>::value;

或者更简单地,直接定义 is_variant_type 如下:

  1. template<typename T, typename Variant>
  2. concept is_variant_type = requires (const Variant& var) {
  3. []<typename... Args>(const std::variant<Args...>&)
  4. requires (std::same_as<T, Args> || ...)
  5. { }(var);
  6. };

演示

英文:

Since variant2 inherits std::variant, you can extract its base variant type through the following helper function

  1. template&lt;typename... Args&gt;
  2. auto as_variant(const std::variant&lt;Args...&gt;&amp;) -&gt; std::variant&lt;Args...&gt;;
  3. template&lt;typename T&gt;
  4. struct to_variant;
  5. template&lt;typename T&gt;
  6. requires requires (const T&amp; t) { as_variant(t); }
  7. struct to_variant&lt;T&gt; {
  8. using type = decltype(as_variant(std::declval&lt;const T&amp;&gt;()));
  9. };

Then slightly modify the is_variant_type concept

  1. template&lt;typename T, typename Variant&gt;
  2. concept is_variant_type =
  3. variant_type&lt;T, typename to_variant&lt;Variant&gt;::type&gt;::value;

Or more simply, just define is_variant_type directly as

  1. template&lt;typename T, typename Variant&gt;
  2. concept is_variant_type = requires (const Variant&amp; var) {
  3. []&lt;typename... Args&gt;(const std::variant&lt;Args...&gt;&amp;)
  4. requires (std::same_as&lt;T, Args&gt; || ...)
  5. { }(var);
  6. };

Demo

huangapple
  • 本文由 发表于 2023年6月12日 22:47:13
  • 转载请务必保留本文链接:https://go.coder-hub.com/76457805.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定