3

As pointed out by Michael Park, adding perfect forwarding constructors can be tricky if we do not want to end up in the wrong constructor.

Currently I have a class A which uses a perfect forwarding constructor, and because of that, I need to declare explicitly the 4 constructors: A&, const A&, A&& and const A&&:

class A
{
public:
    template<typename T> A(T&&);
    A(A&);
    A(const A&);
    A(A&&);
    A(const A&&);
};

I would like to forbid the usage of the const reference rvalue constructor, so I’m considering deleting it:

class A
{
public:
    template<typename T> A(T&&);
    A(A&);
    A(const A&);
    A(A&&);
    A(const A&&) = delete;
};

It looks like it works so far. But I’m just looking at a practical sample, I’d like a better confirmation, e.g. from the C++ standard.

Is it possible that the perfect forwarding constructor will take over the deleted constructor? After all, the universal reference T&& is compatible with const A&&.

vdavid
  • 2,434
  • 1
  • 14
  • 15
  • 2
    I would suggest the opposite approch. Use SFINAE to limit the forwarding constructor and keep the compiler supplied defaults. – NathanOliver Aug 13 '19 at 13:45
  • Perfectly forwarding implicit conversion constructor from anything feels like something that you may be do not want to have? – Öö Tiib Aug 13 '19 at 13:56
  • @NathanOliver can I use SFINAE to tell the compiler to accept "everything but A" in the forwarding constructor? – vdavid Aug 13 '19 at 14:04
  • `template, bool> = true>` says only use this when `T` is not an `A`. – NathanOliver Aug 13 '19 at 14:05
  • @ÖöTiib Actually the goal is to work with "anything", so yes I’m afraid that’s what I want to have. – vdavid Aug 13 '19 at 14:06
  • @NathanOliver Thank you I’ll give it a try. But even if it works the question stays open, i.e. I’d like to know if the explicit deletion of the constructor is guaranteed to forbid the call of an equivalent signature with a forwarding constructor. – vdavid Aug 13 '19 at 14:13
  • @vdavid Ah. Well you have an answer for that below. If there is an exact match constructor, that is always preferred to a template one. The deletion is checked after overload resolution completes so your guaranteed to get an error. I think I have a dupe target for this, let me look. – NathanOliver Aug 13 '19 at 14:17
  • 1
    [This](https://stackoverflow.com/questions/56241988/deleting-move-constructor-and-constructing-object-from-rvalue) is the Q i was thinking off. Not good enough to be a dupe target but confirms that deleted constructors are part of overload resolution and will be called if they are the best match. – NathanOliver Aug 13 '19 at 14:21

1 Answers1

4

Perfect-forwarding constructors are typically better matches for non-const lvalues.

Is it possible that the perfect forwarding constructor will take over the deleted constructor?

No. A(const A&&) is a perfect match and it is explicitly deleted, so, if we try to construct an A from a const lvalue reference, a compile-time error is emmited.

Yet you may do something like this template<typename T, typename = typename std::enable_if<!std::is_same_v<A&&, T>> A(T&&); as well.

P.S. It's a good practice to mark your constructors explicit (especially when type deduction occurs).

Nestor
  • 687
  • 5
  • 12