C++ – Why Does const Type&& Not Cast to Type&&?

c++

I know these two qualifiers are orthogonal, but this creates some weird problems.

Basically, I tried to move-construct a pair of <const T, T> from other pair&& of same type, but got bamboozled as that constructor got ill-formed 🙁

#include <iostream>

struct Special {
    int x = 0;
    explicit Special(int x): x(x) {}
    Special(const Special&) = delete;
    Special& operator=(const Special&) = delete;
    
    Special(Special&&) = default;
    Special& operator=(Special&&) = default;
};

struct NeitherDefaultNorCopyConstructible {
    Special x;

    NeitherDefaultNorCopyConstructible() = delete;
    NeitherDefaultNorCopyConstructible(const NeitherDefaultNorCopyConstructible&) = delete;
    NeitherDefaultNorCopyConstructible& operator=(const NeitherDefaultNorCopyConstructible&) = delete;

    NeitherDefaultNorCopyConstructible(Special&& x): x(std::move(x)) {}
    NeitherDefaultNorCopyConstructible(NeitherDefaultNorCopyConstructible&&) = default;
    NeitherDefaultNorCopyConstructible& operator=(NeitherDefaultNorCopyConstructible&&) = default;

    bool operator==(const NeitherDefaultNorCopyConstructible& other) const {
        return x.x == other.x.x;
    }
};

int main() {
    using NodeType = std::pair<const NeitherDefaultNorCopyConstructible, NeitherDefaultNorCopyConstructible>;
    NodeType a(Special(0), Special(1));
    NodeType b(std::move(a));
    return 0;
}

Error message:

/usr/include/c++/11/bits/stl_pair.h:315:17: note: ‘constexpr std::pair<_T1, _T2>::pair(std::pair<_T1, _T2>&&) [with _T1 = const NeitherDefaultNorCopyConstructible; _T2 = NeitherDefaultNorCopyConstructible]’ is implicitly deleted because the default definition would be ill-formed:
  315 |       constexpr pair(pair&&) = default;         ///< Move constructor

/home/bibaboba/CLionProjects/untitled4/main.cpp:32:28: error: use of deleted function ‘constexpr std::pair<_T1, _T2>::pair(std::pair<_T1, _T2>&&) [with _T1 = const NeitherDefaultNorCopyConstructible; _T2 = NeitherDefaultNorCopyConstructible]’
   32 |     NodeType b(std::move(a));

Do I really have to dissolve the pair to move it, or is there any way to avoid it?

Best Answer

std::move is simply a cast that returns an xvalue expression. Expecting it to cast away const is a bit of an over stretch.

std::move will cast an object of const Type to const Type&&. That’s all it does.

basically, I tried to move-construct a pair of <const T, T> from other pair&& of same type, but got bamboozled as that constructor got ill-formed.

The ctor didn’t get ill formed. The ctor just doesn’t exist and the ctor the compiler resorted to was deleted hence the compiler got stuck and decided to throw errors.

You can choose to either provide a const Type&& ctor (not my best recommendation) or cast away const before std::move.