使用std::visit与已删除函数的特化

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

using std::visit with specialization of a deleted function

问题

#include <iostream>
#include <variant>
#include <vector>
#include <charconv>

template <class ...Request, class ...Types>
constexpr bool holdsAlternative(const std::variant<Types...>& v) noexcept
{
    return (std::holds_alternative<Request>(v) || ...);
}
template <typename T>
T convert(const std::string &str) = delete;

template <>
float convert<float>(const std::string &str)
{
    float result = 0;
    std::from_chars(str.data(), str.data() + str.size(), result);
    return result;
}
template <>
int32_t convert<int32_t >(const std::string &str)
{
    int32_t result = 0;
    std::from_chars(str.data(), str.data() + str.size(), result);
    return result;
}
using data_type = std::variant<int32_t, float, std::string>;
std::vector<data_type> Types {float{}, int32_t{}, std::string{}};
int main()
{
    std::string str{"123"};
    bool r = holdsAlternative<int32_t, float>(Types[1]);
    if (r)
    {
       std::visit([&](auto &t) { return convert<decltype(t)>(str); }, Types[1]);
    }
}
英文:

I am trying to parse a string generically based on some prior knowledge.
str is passed to convert<T>() with overloads for the types I want to support, and try to use std::visit() to call the right overload:

#include &lt;iostream&gt;
#include &lt;variant&gt;
#include &lt;vector&gt;
#include &lt;charconv&gt;

template &lt;class ...Request, class ...Types&gt;
constexpr bool holdsAlternative(const std::variant&lt;Types...&gt;&amp; v) noexcept
{
   return (std::holds_alternative&lt;Request&gt;(v) || ...);
}
template &lt;typename T&gt;
T convert(const std::string &amp;str) = delete;

template &lt;&gt;
float convert&lt;float&gt;(const std::string &amp;str)
{
    float result = 0;
    std::from_chars(str.data(), str.data() + str.size(), result);
    return result;
}
template &lt;&gt;
int32_t convert&lt;int32_t &gt;(const std::string &amp;str)
{
    int32_t result = 0;
    std::from_chars(str.data(), str.data() + str.size(), result);
    return result;
}
using data_type = std::variant&lt;int32_t, float, std::string&gt;;
std::vector&lt;data_type&gt; Types {float{}, int32_t{}, std::string{}};
int main()
{
    std::string str{&quot;123&quot;};
    bool r = holdsAlternative&lt;int32_t, float&gt;(Types[1]);
    if (r)
    {
       std::visit([&amp;](auto &amp;t) { return convert&lt;decltype(t)&gt;(str); }, Types[1]);
    }
}

答案1

得分: 2

有你的代码存在三个问题:

  1. 访问者必须适用于所有可能的变体,因此删除非专用的convert不起作用。但你可以使非专用版本抛出运行时异常。

  2. 传递给std::visit的访问者必须对所有变体具有相同的返回类型。这取决于你想要做什么。如果你想通过convert的输出设置变体值,那么没问题:所有访问者都可以返回void

  3. 你应该将std::decay_t&lt;decltype(t)&gt;作为访问者的模板参数传递,因为否则decltype(t)将被推断为引用,而你没有针对例如float&amp;的特化。

这里是修复后的代码:https://godbolt.org/z/abqv6xKde

#include <iostream>
#include <variant>
#include <vector>
#include <charconv>

template <class ...Request, class ...Types>
constexpr bool holdsAlternative(const std::variant<Types...>& v) noexcept
{
    return (std::holds_alternative<Request>(v) || ...);
}

template <typename T>
T convert(const std::string &str){
    throw std::logic_error("Conversion not supported\n");
};

template <>
float convert<float>(const std::string &str)
{
    std::cout << "float\n";
    float result = 0;
    std::from_chars(str.data(), str.data() + str.size(), result);
    return result;
}
template <>
int32_t convert<int32_t >(const std::string &str)
{
    std::cout << "int32_t\n";
    int32_t result = 0;
    std::from_chars(str.data(), str.data() + str.size(), result);
    return result;
}

using data_type = std::variant<int32_t, float, std::string>;

std::vector<data_type> Types {float{}, int32_t{}, std::string{}};

int main()
{
    std::string str{"123"};
    bool r = holdsAlternative<int32_t, float>(Types[1]);
    if (r)
    {
        std::visit([&](auto &t) { t = convert<std::decay_t<decltype(t)>>(str); }, Types[1]);
    }
}
英文:

There are three issues with your code:

  1. The visitor must be applicable to all possible variants, so deleting the non-specialized convert won't work. You can however have the non-specialized version throw a runtime exception.
  2. The visitor passed to std::visit must have the same return type for all variants. If this is an issue for you depends on what you want to do. If you want to set the variant value by the output of convert, you are fine: The visitors could all return void.
  3. You should pass std::decay_t&lt;decltype(t)&gt; as the template parameter in the visitor, because decltype(t) would otherwise be deduced as a reference, and you don't have specializations for e.g. float&amp;.

Here is the fixed code: https://godbolt.org/z/abqv6xKde

#include &lt;iostream&gt;
#include &lt;variant&gt;
#include &lt;vector&gt;
#include &lt;charconv&gt;

template &lt;class ...Request, class ...Types&gt;
constexpr bool holdsAlternative(const std::variant&lt;Types...&gt;&amp; v) noexcept
{
   return (std::holds_alternative&lt;Request&gt;(v) || ...);
}

template &lt;typename T&gt;
T convert(const std::string &amp;str){
    throw std::logic_error(&quot;Conversion not supported\n&quot;);
};

template &lt;&gt;
float convert&lt;float&gt;(const std::string &amp;str)
{
    std::cout &lt;&lt; &quot;float\n&quot;;
    float result = 0;
    std::from_chars(str.data(), str.data() + str.size(), result);
    return result;
}
template &lt;&gt;
int32_t convert&lt;int32_t &gt;(const std::string &amp;str)
{
    std::cout &lt;&lt; &quot;int32_t\n&quot;;
    int32_t result = 0;
    std::from_chars(str.data(), str.data() + str.size(), result);
    return result;
}

using data_type = std::variant&lt;int32_t, float, std::string&gt;;

std::vector&lt;data_type&gt; Types {float{}, int32_t{}, std::string{}};

int main()
{
    std::string str{&quot;123&quot;};
    bool r = holdsAlternative&lt;int32_t, float&gt;(Types[1]);
    if (r)
    {
       std::visit([&amp;](auto &amp;t) { t = convert&lt;std::decay_t&lt;decltype(t)&gt;&gt;(str); }, Types[1]);
    }
}

huangapple
  • 本文由 发表于 2023年3月7日 06:08:06
  • 转载请务必保留本文链接:https://go.coder-hub.com/75656286.html
匿名

发表评论

匿名网友

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

确定