I have a variadic variant_callable
class object that I want to use for a runtime polymorphism. Inside it uses a visitor pattern with std::variant
.
However, I came by a rather strange behavior, that is object's destructor is called twice!.
#include <utility>
#include <variant>
#include <tuple>
namespace detail
{
template<typename... Impl>
class variadic_callable
{
public:
template<typename T>
constexpr explicit variadic_callable(T &&t) //
: varImpl_(std::forward<T>(t))
{}
variadic_callable(const variadic_callable &) = delete;
variadic_callable(variadic_callable &&) = delete;
template<typename... Args>
constexpr decltype(auto) operator()(Args &&...args) const
{
return std::visit(
[argsTuple = std::forward_as_tuple(args...)](const auto &visitor) {
return std::apply(
[&visitor](auto &&...args) {
return visitor(std::forward<decltype(args)>(args)...);
},
argsTuple);
},
varImpl_);
}
private:
std::variant<Impl...> varImpl_;
};
} // namespace detail
#include <string>
#include <iostream>
int main(int, char **)
{
struct callable
{
std::string str = "Long enough string to be allocated. Oceanic";
callable()
{
std::cout << "callable()" << std::endl;
}
void operator()(int i) const
{
std::cout << str << " " << i << '\n';
}
~callable()
{
std::cout << "~callable()" << std::endl;
}
};
{
std::cout << "expcected:\n";
const auto &c = callable();
c(815);
std::cout << "finished\n";
}
std::cout << '\n';
{
std::cout << "actual\n";
const auto &w = detail::variadic_callable<callable>{callable()};
w(815);
std::cout << "finished\n";
}
}
The output:
Program returned: 0
expcected:
callable()
Long enough string to be allocated. Oceanic 815
finished
~callable()
actual
callable()
~callable()
Long enough string to be allocated. Oceanic 815
finished
~callable()
https://godbolt.org/z/d849EaqbE
I guess an UB is in-place, but I can't spot it.
What I find the most peculiar is the fact that in the "actual" case std::string
resources are not destroyed after the first destructor invocation!