Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is it not possible to instantiate pair with "non const" copy constructor while it is possible to instantiate one without?

Suppose you have the following class:

struct A {
    A () {}
    A (A &) = delete;
};

int main() {
    std::pair<A, int> p1;
    return 0;
}

The following code will fail to compile (using -std=c++11 with g++) with the following error:

/usr/include/c++/5/bits/stl_pair.h: In instantiation of ‘struct std::pair’:

test.cpp:13:23: required from here

/usr/include/c++/5/bits/stl_pair.h:127:17: error: ‘constexpr std::pair<_T1, _T2>::pair(const std::pair<_T1, _T2>&) [with _T1 = A; _T2 = int]’ declared to take const reference, but implicit declaration would take non-const

constexpr pair(const pair&) = default;

According to the error message, I would assume that this is because it is not possible to instanciate the default copy constructor because of the const qualifier on the std::pair argument.

I could understand why this would not compile without the = delete, because it is not possible to instanciate the copy constructor which takes a std::pair const& parameter.

But with the = delete, I would expect the compiler to not instantiate such constructor because it cannot (as far as I understand). Actually, this copy constructor is deleted as shown by this piece of code:

std::pair<A, int> p1;
decltype(p1) p2(p1);

Which fails:

test.cpp:11:23: error: use of deleted function ‘constexpr std::pair<_T1, _T2>::pair(const std::pair<_T1, _T2>&) [with _T1 = A; _T2 = int]’

decltype(p1) p2(p1);

Basically, my question is: Why does the compiler fails to instantiate a deleted copy constructor of std::pair?

like image 861
Holt Avatar asked Mar 25 '16 15:03

Holt


1 Answers

If you want to = delete the copy constructor, the proper form is: A(const A&) = delete;. See how you forgot that const? pair can be used with a non-copyable type, even a non-moveable type.

Your instantiation problem can also be demonstrated with:

struct A {
    A(A&) = delete;
};

struct B : A {
    B(const B&) = default;
};

or...

struct B {
    B(const B&) = default;
    A a;
};

Basically, pair = default its copy ctor, and defines it as taking a const pair&, but one one of your types defines it to take a non-const &, so the default generation fails since it can't pass it's const& to your non-const&. Even though yours is = deleteed, it doesn't matter, it doesn't get that far.

A defaulted copy ctor will effectively be deleted if a member or base class is non-copyable. But in order to figure that out, it has to have a function it is able to call (even if that function is deleted). In these cases, it can't pass a const& to a non-const& so it can't even figure out that you have a deleted copy ctor. Conceivably, something in the standard could be written to accommodate this edge case.

like image 180
David Avatar answered Sep 20 '22 17:09

David