CodeNebula
C++20stladvanced

std::move_constructible (C++20 Addition)

C++20 · The Big Four · Compiler flag: -std=c++20

Explanation

Concept that checks if a type can be constructed from an rvalue of itself. Essential for types that participate in move semantics, enabling efficient transfer of resources.

Real-World Scenario

Constrain a task queue to only accept task types that can be moved into the queue, ensuring efficient ownership transfer.

Quick Snippet

snippet.cpp
#include <concepts>
#include <queue>

template<std::move_constructible Task>
class TaskQueue {
    std::queue<Task> tasks;
public:
    void enqueue(Task task) { tasks.push(std::move(task)); }
};

Full Example

move_constructible.cpp
// CodeNebula - C++20 Example: std::move_constructible Concept
// Compile: g++ -std=c++20 -Wall -Wextra -o example move_constructible.cpp

// std::move_constructible<T> is satisfied when T can be constructed from
// an rvalue of type T. Most types satisfy this; types with deleted move
// and copy constructors do not.

#include <concepts>
#include <iostream>
#include <memory>
#include <string>
#include <vector>

// A move-only resource type
struct UniqueBuffer {
    std::unique_ptr<int[]> data;
    std::size_t size;

    UniqueBuffer(std::size_t n) : data(std::make_unique<int[]>(n)), size(n) {
        for (std::size_t i = 0; i < n; ++i) data[i] = static_cast<int>(i);
    }
    // Move constructor enabled, copy deleted (via unique_ptr)
};

// Not move constructible
struct Pinned {
    int value = 42;
    Pinned() = default;
    Pinned(Pinned&&) = delete;
    Pinned(const Pinned&) = delete;
};

// Transfer ownership of a move-constructible resource
template <std::move_constructible T>
T take_ownership(T&& resource) {
    std::cout << "  Ownership transferred\n";
    return std::move(resource);
}

// Collect items into a vector (requires move_constructible)
template <std::move_constructible T>
std::vector<T> collect(T&& first, T&& second) {
    std::vector<T> result;
    result.push_back(std::move(first));
    result.push_back(std::move(second));
    return result;
}

template <typename T>
void check_move(const char* name) {
    std::cout << name << ": " << std::move_constructible<T> << "\n";
}

int main() {
    // Transfer string ownership
    std::string s = "hello";
    std::cout << "Moving string:\n";
    auto s2 = take_ownership(std::move(s));
    std::cout << "  Result: " << s2 << "\n";

    // Transfer unique_ptr ownership
    std::cout << "Moving unique_ptr:\n";
    auto p = std::make_unique<int>(99);
    auto p2 = take_ownership(std::move(p));
    std::cout << "  Value: " << *p2 << "\n";

    // Collect items
    auto items = collect(std::string("foo"), std::string("bar"));
    std::cout << "Collected: " << items[0] << ", " << items[1] << "\n";

    // Concept checks
    std::cout << "\nMove constructible checks:\n";
    check_move<int>("int");
    check_move<std::string>("string");
    check_move<std::unique_ptr<int>>("unique_ptr<int>");
    check_move<UniqueBuffer>("UniqueBuffer");
    check_move<Pinned>("Pinned");

    return 0;
}

// Expected output:
// Moving string:
//   Ownership transferred
//   Result: hello
// Moving unique_ptr:
//   Ownership transferred
//   Value: 99
// Collected: foo, bar
//
// Move constructible checks:
// int: 1
// string: 1
// unique_ptr<int>: 1
// UniqueBuffer: 1
// Pinned: 0
Run command
g++ -std=c++20 -Wall -Wextra -o example move_constructible.cpp