This code works as expected:
#include <iostream>
#include <tuple>
int main() {
std::tuple<int> t1{3};
std::tuple<int> t2{t1};
std::cout << std::get<0>(t2) << std::endl;
return 0;
}
It correctly prints the number 3. But this code doesn’t:
#include <any>
#include <iostream>
#include <tuple>
int main() {
std::tuple<int> t1{3};
std::tuple<std::any> t2{t1};
std::cout << std::any_cast<int>(std::get<0>(t2)) << std::endl;
return 0;
}
std::any_cast<int>
throws the exception std::bad_any_cast
. However, if I add one more element to the tuples it does work:
#include <any>
#include <iostream>
#include <tuple>
int main() {
std::tuple<int, float> t1{3, 3.14};
std::tuple<std::any, std::any> t2{t1};
std::cout << std::any_cast<int>(std::get<0>(t2)) << std::endl;
return 0;
}
I compiled everything with C++23.
I inspected the type of std::get<0>(t2)
:
#include <any>
#include <iostream>
#include <tuple>
int main() {
std::tuple<int> t1{3};
std::tuple<std::any> t2{t1};
std::cout << std::get<0>(t2).type().name() << std::endl;
return 0;
}
Compiling with Clang on Linux gave me the output St5tupleIJiEE
, which the LLVM demangler shows as std::tuple<int>
. So what is happening is that the tuple t1
is being copied to the std::any
inside t2
, while what I expected was the tuple itself t1
to be copied and the int
going into std::any
. This only happens when using a unary tuple with std::any
, otherwise the behavior is as expected.
The cppreference page for the constructors of std::tuple
has the following key constructors:
tuple( const Types&... args ); // (2)
template< class... UTypes >
constexpr tuple( tuple<UTypes...>& other ); // (4)
template< class... UTypes >
tuple( const tuple<UTypes...>& other ); // (5)
It seems that the unary tuple t2
is being constructed with (2) instead of (4) or (5), while the tuple with two elements is being constructed with (4) or (5).
So I have two questions
t2
and then use the assignment operator to copy t1
into it, but I don’t want to do that since I might want to use std::move
for performance but the code fails even in this case.As @NathanOliver comments, you are trying to use converting operator (5), which requires std::tuple_size > 1
.
You can work around this issue by constructing the second tuple from the first's argument: std::tuple<std::any> t2{ std::get<0>(t1) };
.
If you are working in some generic code which needs to handle tuple with any number of std::any
, then you need a slightly unwieldy:
auto t2 = std::apply([](auto&& args) {
return std::make_tuple(std::any{ std::forward<decltype(args)>(args) }...);
}, t1);