Using boost:variant
:
#include <tuple>
#include <iostream>
#include <boost/variant.hpp>
template <size_t n, typename... T>
boost::variant<T...> _tuple_index(size_t i, const std::tuple<T...>& tpl) {
if (i == n)
return std::get<n>(tpl);
else if (n == sizeof...(T) - 1)
throw std::out_of_range("Out of Index");
else
return _tuple_index<(n < sizeof...(T)-1 ? n+1 : 0)>(i, tpl);
}
template <typename... T>
boost::variant<T...> tuple_index(size_t i, const std::tuple<T...>& tpl) {
return _tuple_index<0>(i, tpl);
}
template <typename T>
auto tuple_len(T &tpl) {
return std::tuple_size<T>::value;
}
int main()
{
std::tuple<std::string, double, double, int> t("123", 4.5, 6.7, 8);
for(int i = 0; i != tuple_len(t); ++i) {
std::cout << tuple_index(i, t) << std::endl; // works with boost
}
}
Replace boost::variant
by std::variant
, added a helper to stream std::variant
:
#include <tuple>
#include <iostream>
#include <variant>
template <size_t n, typename... T>
std::variant<T...> _tuple_index(size_t i, const std::tuple<T...>& tpl) {
if (i == n)
return std::get<n>(tpl);
else if (n == sizeof...(T) - 1)
throw std::out_of_range("Out of Index");
else
return _tuple_index<(n < sizeof...(T)-1 ? n+1 : 0)>(i, tpl);
}
template <typename... T>
std::variant<T...> tuple_index(size_t i, const std::tuple<T...>& tpl) {
return _tuple_index<0>(i, tpl);
}
template <typename T>
auto tuple_len(T &tpl) {
return std::tuple_size<T>::value;
}
// added helper to stream std::variant
template <typename T0, typename ... Ts>
std::ostream & operator<< (std::ostream & s, std::variant<T0, Ts...> const & v) {
std::visit([&](auto && arg){ s << arg;}, v);
return s;
}
int main()
{
std::tuple<std::string, double, double, int> t("123", 4.5, 6.7, 8);
for(int i = 0; i != tuple_len(t); ++i) {
std::cout << tuple_index(i, t) << std::endl; // doesn't work anymore
}
}
The compilation remain error:
$ clang++ -v [17:37:47]
Apple LLVM version 10.0.1 (clang-1001.0.46.4)
Target: x86_64-apple-darwin18.6.0
Thread model: posix
InstalledDir: /Library/Developer/CommandLineTools/usr/bin
$ clang++ -std=c++17 isostd.cpp
isostd.cpp:8:16: error: no viable conversion from returned value of type 'const typename tuple_element<1UL, tuple<basic_string<char>, double, double, int> >::type' (aka 'const __type_pack_element<1UL, std::__1::basic_string<char>, double, double, int>') to function return
type 'std::variant<basic_string<char>, double, double, int>'
return std::get<n>(tpl);
^~~~~~~~~~~~~~~~
isostd.cpp:12:16: note: in instantiation of function template specialization '_tuple_index<1, std::__1::basic_string<char>, double, double, int>' requested here
return _tuple_index<(n < sizeof...(T)-1 ? n+1 : 0)>(i, tpl);
^
isostd.cpp:16:12: note: in instantiation of function template specialization '_tuple_index<0, std::__1::basic_string<char>, double, double, int>' requested here
return _tuple_index<0>(i, tpl);
^
isostd.cpp:35:22: note: in instantiation of function template specialization 'tuple_index<std::__1::basic_string<char>, double, double, int>' requested here
std::cout << tuple_index(i, t) << std::endl; // doesn't work anymore
^
/Library/Developer/CommandLineTools/usr/include/c++/v1/variant:1142:3: note: candidate constructor not viable: no known conversion from 'const typename tuple_element<1UL, tuple<basic_string<char>, double, double, int> >::type'
(aka 'const __type_pack_element<1UL, std::__1::basic_string<char>, double, double, int>') to 'const std::__1::variant<std::__1::basic_string<char>, double, double, int> &' for 1st argument
variant(const variant&) = default;
^
/Library/Developer/CommandLineTools/usr/include/c++/v1/variant:1143:3: note: candidate constructor not viable: no known conversion from 'const typename tuple_element<1UL, tuple<basic_string<char>, double, double, int> >::type'
(aka 'const __type_pack_element<1UL, std::__1::basic_string<char>, double, double, int>') to 'std::__1::variant<std::__1::basic_string<char>, double, double, int> &&' for 1st argument
variant(variant&&) = default;
^
/Library/Developer/CommandLineTools/usr/include/c++/v1/variant:1155:13: note: candidate template ignored: substitution failure [with _Arg = const double &, $1 = 0, $2 = 0, $3 = 0, _Tp = double]: no member named 'value' in
'std::__1::__find_detail::__find_unambiguous_index_sfinae<double, std::__1::basic_string<char>, double, double, int>'
constexpr variant(_Arg&& __arg) noexcept(
^
1 error generated.
How can I properly replace boost:variant by std::variant?
I aware of the reference: What are the differences between std::variant and boost::variant?
Boost.Variant includes recursive_variant, which allows a variant to contain itself. They're essentially special wrappers around a pointer to a boost::variant, but they are tied into the visitation machinery.
If I understand correctly, there is no way to finish the replace?
As you have duplicate types in your variant some of the constructors are disabled:
This overload only participates in overload resolution if there is exactly one occurrence of T in Types...
You need to use the constructor with an explicit type index:
return std::variant<T...>(std::in_place_index<n>, std::get<n>(tpl));
Your original boost code has undefined behaviour:
Each type specified as a template argument to variant must be distinct after removal of qualifiers. Thus, for instance, both
variant<int, int>
andvariant<int, const int>
have undefined behavior.
The standard libary implementation does support duplicate types and therefore prevents you from accidentally constructing an ambigous variant. For example what should the following do:
variant<std::string, double, double, int> t = 4.5;
With boost it is UB, either of the double
values might be initialised or it might do something completely different. The standard library explitly makes this a compiler error so that you have to choose which of your double
s you want to initialise.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With