In the following snippet I have reconstructed a small failing example of what I'm working on right now. The class entity should be able to gobble up different "functor" types, hence it has an universal reference argument.
However, it seems that this template will even be called for when I clearly want to only use the copy constructor as in entity a{n}; In my final code this leads to infinite recursion. Why is this happening and how can I prevent it? I have even explicitely excluded the entity type from the overload, but to no avail...
#include <cstdio>
#include <utility>
struct functor_t
{
void* something_;
};
struct entity
{
entity()
{
functor_ = new functor_t();
}
entity(functor_t* functor)
: functor_{ functor }
{
printf("entity(member_t)\n");
}
~entity() {
// delete resource
printf("~entity()\n");
delete functor_;
}
template <typename T> requires (!std::same_as<std::remove_cvref<T>, entity>)
entity(T&& other) { // <-- why you??
printf("entity(T&&)\n");
// functor_ = new functor_t(/* */);
}
entity(const entity& other) {
printf("entity(const entity&)\n");
functor_ = new functor_t(other.functor_);
}
entity(entity&& other) {
printf("entity(entity&&)\n");
functor_ = new functor_t(std::move(other.functor_));
}
template <typename T>
auto operator=(T&& other) -> entity& {
entity{std::forward<T>(other)}.swap(*this);
return *this;
}
auto swap(entity& other) -> void {
std::swap(functor_, other.functor_);
}
functor_t* functor_ = nullptr;
};
int main()
{
entity n;
entity a{n};
entity b;
b = a;
printf("--------------------\n");
}
Yields:
entity(T&&)
entity(T&&)
~entity()
--------------------
~entity()
~entity()
~entity()
Even though I don't want to call the universal constructor.
The template constructor is the best candidate for overload resolution because it doesn't require any conversions. The copy constructor accepts a reference to const qualified
T, and the given expression is an lvalue of non-constTwhich requires an implicit conversion and hence that overload is less preferred.The
requiresclause doesn't disqualify the template constructor becausestd::same_as<std::remove_cvref<entity&>is not the same class asentity. You probably intended to usestd::same_as<std::remove_cvref_t<entity&>instead.Fix the requires clause, as per previous paragraph.
Providing the constructor
entity(entity&)would also work, but I don't recommend that approach.