Reputation: 25623
I want to make my class be able to be used in a std::variant
.
The simply code that should work is:
int main()
{
std::variant< int, A > v;
A a(1);
v = a;
}
My class contains a templated constructor:
template <typename T> A( T& );
At this point the trouble starts! The constructor binds to the call from std::variant
and not the provided A(const A&)
is used anymore.
For copy&paste reasons the full example here:
#include <iostream>
#include <variant>
class A
{
private:
int x;
public:
A( A&&) {}
A( const A& ) {}
A(){}
~A() {}
A& operator=( const A& ) { return *this;}
A& operator=( A&& ) {return *this;}
template <typename T>
A( T& t )
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
A(int _x):x{_x}{}
};
int main()
{
std::variant< int, A > v;
A a(1);
v = a;
}
Background:
Why the template here? The problem starts while using a constructor which takes a serializer type. The serializer can have multiple types, depends on files or streams to serialize with.
Remark: I know that the functionality of the constructors is missing!
Upvotes: 2
Views: 121
Reputation: 119382
The problem is not with std::variant
. The problem is with the constructor template,
template <typename T>
A(T& t)
Such constructors are problematic because when the argument is a non-const
lvalue of type A
, this constructor is preferred over the copy constructor taking const A&
---which is usually not the intended behaviour. To prevent this, we usually constrain this constructor with SFINAE:
template <typename T, typename = std::enable_if_t<!std::is_same_v<std::decay_t<T>, A>>>
A(T& t) // or T&& t
and might consider making it explicit
as well.
We usually do not provide copy constructors taking non-const
A&
, since they are redundant next to the ones taking const A&
.
Upvotes: 3