英文:
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 <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]);
}
}
答案1
得分: 2
有你的代码存在三个问题:
-
访问者必须适用于所有可能的变体,因此删除非专用的
convert
不起作用。但你可以使非专用版本抛出运行时异常。 -
传递给
std::visit
的访问者必须对所有变体具有相同的返回类型。这取决于你想要做什么。如果你想通过convert
的输出设置变体值,那么没问题:所有访问者都可以返回void
。 -
你应该将
std::decay_t<decltype(t)>
作为访问者的模板参数传递,因为否则decltype(t)
将被推断为引用,而你没有针对例如float&
的特化。
这里是修复后的代码: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:
- 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. - 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 ofconvert
, you are fine: The visitors could all returnvoid
. - You should pass
std::decay_t<decltype(t)>
as the template parameter in the visitor, becausedecltype(t)
would otherwise be deduced as a reference, and you don't have specializations for e.g.float&
.
Here is the fixed code: 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]);
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论