C ++: operator << không biên dịch được

Aug 19 2020

Tôi đang viết mã B-tree trong C ++, nhưng khi tôi quá tải operator<<, nó không biên dịch được.

Mã của tôi:

#include <cassert>
#include <cstddef>
#include <iostream>
#include <memory>
#include <vector>
#include <utility>

template <typename T, std::size_t t>
class BTree;

template <typename T, std::size_t t>
std::ostream& operator<<(std::ostream&, const BTree<T, t>&);

template <typename T, std::size_t t>
std::ostream& operator<<(std::ostream& os, const typename BTree<T, t>::Node& node);

template <typename T, std::size_t t>
class BTree {
    static_assert(t >= 2);
    class Node {
        std::size_t n = 0;
    public:
        bool leaf = true;
        std::vector<T> key;
        std::vector<std::unique_ptr<Node>> child;
        void setN(std::size_t N) {
            n = N;
            key.resize(n);
            if (!leaf) {
                child.resize(n + 1);
            }
        }

        [[nodiscard]] std::size_t getN() const {
            return n;
        }

        [[nodiscard]] bool isFull() const {
            return n == 2 * t - 1;
        }

        friend std::ostream& operator<< <T, t>(std::ostream& os, const BTree<T, t>::Node& node);
    };

    std::unique_ptr<Node> root;

    std::pair<const Node*, std::size_t> Search(const Node* x, const T& k) const {
        std::size_t i = 0;
        while (i < x->getN() && k > x->key[i]) {
            i++;
        }
        if (i < x->getN() && k == x->key[i]) {
            return {x, i};
        } else if (x->leaf) {
            return {nullptr, 0};
        } else {
            return Search(x->child[i].get(), k);
        }
    }

    void SplitChild(Node* x, std::size_t i) {
        if (!x) {
            return;
        }
        auto y = x->child[i].get();
        if (!y) {
            return;
        }
        assert(!x->isFull() && y->isFull());
        auto z = std::make_unique<Node>();
        z->leaf = y->leaf;
        z->setN(t - 1);
        for (std::size_t j = 0; j < t - 1; j++) {
            z->key[j] = y->key[j + t];
        }
        if (!y->leaf) {
            for (std::size_t j = 0; j < t; j++) {
                z->child[j] = std::move(y->child[j + t]);
            }
        }
        x->setN(x->getN() + 1);
        for (std::size_t j = x->getN() + 1; j >= i + 1; j--) {
            x->child[j + 1] = std::move(x->child[j]);
        }
        x->child[i + 1] = std::move(z);
        for (std::size_t j = x->getN() + 1; j >= i + 1; j--) {
            x->key[j] = x->key[j - 1];
        }
        x->key[i] = y->key[t];
        y->setN(t - 1);
    }

    void InsertNonFull(Node* x, const T& k) {
        std::size_t i = x->getN();
        if (x->leaf) {
            x->setN(i + 1);
            while (i < x->getN() && k < x->key[i]) {
                x->key[i + 1] = x->key[i];
                i--;
            }
            x->key[i + 1] = k;
        } else {
            while (i < x->getN() && k < x->key[i]) {
                i--;
            }
            i++;
            if (x->child[i]->isFull()) {
                SplitChild(x, i);
                if (k > x->key[i]) {
                    i++;
                }
            }
            InsertNonFull(x->child[i].get(), k);
        }

    }

public:
    BTree() {
        root = std::make_unique<Node>();
    }

    [[nodiscard]] std::pair<const Node*, std::size_t> Search(const T& k) const {
        return Search(root.get(), k);
    }

    void Insert(const T& k) {
        if (root->isFull()) {
            auto s = std::make_unique<Node>();
            s->leaf = false;
            s->setN(0);
            s->child[0] = std::move(root);
            root = std::move(s);
            SplitChild(root.get(), 0);
            InsertNonFull(root.get(), k);
        } else {
            InsertNonFull(root.get(), k);
        }
    }


    friend std::ostream& operator<<<> (std::ostream&, const BTree<T, t>&);

};

template <typename T, std::size_t t>
std::ostream& operator<<(std::ostream& os, const typename BTree<T, t>::Node& node) {
    if (node->leaf) {
        for (std::size_t i = 0; i < node->getN() - 1; i++) {
            os << node->key[i] << ' ';
        }
        os << node->key[node->getN() - 1];
    } else {
        for (std::size_t i = 0; i < node->getN(); i++) {
            os << *node->child[i] << ' ' << node->key[i] << ' ';
        }
        os << *node->child[node->getN()];
    }
    return os;
}

template <typename T, std::size_t t>
std::ostream& operator<<(std::ostream& os, const BTree<T, t>& bt) {
    os << *(bt.root) << '\n';
    return os;
}

int main() {
    BTree<int, 2> tree;
    tree.Insert(1);
    std::cout << tree;

}

Đây là dòng đầu tiên của đơn khiếu nại từ trình biên dịch của tôi (Tôi sẽ không đăng toàn bộ kết quả lỗi vì nó có hơn 200 dòng):

/mnt/c/Users/kim/CLionProjects/PPP/main.cpp: In instantiation of ��std::ostream& operator<<(std::ostream&, const BTree<T, t>&) [with T = int; long unsigned int t = 2; std::ostream = std::basic_ostream<char>]��:
/mnt/c/Users/kim/CLionProjects/PPP/main.cpp:171:18:   required from here
/mnt/c/Users/kim/CLionProjects/PPP/main.cpp:164:8: error: no match for ��operator<<�� (operand types are ��std::ostream�� {aka ��std::basic_ostream<char>��} and ��BTree<int, 2>::Node��)
  164 |     os << *(bt.root) << '\n';
      |     ~~~^~~~~~~~~~~~~

btBTree<T, t>, bt.rootstd::unique_ptr<BTree<T, t>::Node>, vì vậy tôi nghĩ toán tử hội nghị nên khớp với operator<<(std::ostream& os, const typename BTree<T, t>::Node& node), nhưng trình biên dịch của tôi vẫn không hài lòng.

Làm thế nào tôi có thể sửa lỗi này? Cảm ơn trước.

Trả lời

4 songyuanyao Aug 19 2020 at 20:33

operator<<đối với lớp bên trong Nodeđược định nghĩa là mẫu, nhưng không thể suy ra các tham số mẫu vì ngữ cảnh không được suy luận .

Trong các trường hợp sau, các kiểu, mẫu và giá trị không phải kiểu được sử dụng để soạn P không tham gia vào suy ra đối số mẫu, nhưng thay vào đó, sử dụng các đối số mẫu đã được suy ra ở nơi khác hoặc được chỉ định rõ ràng. Nếu tham số mẫu chỉ được sử dụng trong các ngữ cảnh không được suy luận và không được chỉ định rõ ràng, thì việc suy diễn đối số mẫu sẽ không thành công.

  1. Các lồng-name-specifier (tất cả mọi thứ ở bên trái của toán tử phân giải phạm vi ::) của một kiểu đó đã được chỉ định sử dụng một trình độ-id :

Bạn có thể định nghĩa nó là non-template và định nghĩa nó trong định nghĩa lớp. Ví dụ

template <typename T, std::size_t t>
class BTree {
    ...
    class Node {
        ...
        friend std::ostream& operator<< (std::ostream& os, const Node& node) {
            if (node->leaf) {
                for (std::size_t i = 0; i < node->getN() - 1; i++) {
                    os << node->key[i] << ' ';
                }
                os << node->key[node->getN() - 1];
            } else {
                for (std::size_t i = 0; i < node->getN(); i++) {
                    os << *node->child[i] << ' ' << node->key[i] << ' ';
                }
                os << *node->child[node->getN()];
            }
            return os;
        }
        ...
    };
    ...
};
frozenca Aug 19 2020 at 20:37

Chà, các mẫu C ++ thật khó hiểu và ngu ngốc.

Mã làm việc:

#include <cassert>
#include <cstddef>
#include <iostream>
#include <memory>
#include <vector>
#include <utility>

template <typename T, std::size_t t>
class BTree {
    static_assert(t >= 2);
    class Node {
        std::size_t n = 0;
    public:
        bool leaf = true;
        std::vector<T> key;
        std::vector<std::unique_ptr<Node>> child;
        void setN(std::size_t N) {
            n = N;
            key.resize(n);
            if (!leaf) {
                child.resize(n + 1);
            }
        }

        [[nodiscard]] std::size_t getN() const {
            return n;
        }

        [[nodiscard]] bool isFull() const {
            return n == 2 * t - 1;
        }

        friend std::ostream& operator<<(std::ostream& os, const BTree<T, t>::Node& node) {
            if (node.leaf) {
                for (std::size_t i = 0; i < node.getN() - 1; i++) {
                    os << node.key[i] << ' ';
                }
                os << node.key[node.getN() - 1];
            } else {
                for (std::size_t i = 0; i < node.getN(); i++) {
                    os << *node.child[i] << ' ' << node.key[i] << ' ';
                }
                os << *node.child[node.getN()];
            }
            return os;
        }
    };

    std::unique_ptr<Node> root;

    std::pair<const Node*, std::size_t> Search(const Node* x, const T& k) const {
        std::size_t i = 0;
        while (i < x->getN() && k > x->key[i]) {
            i++;
        }
        if (i < x->getN() && k == x->key[i]) {
            return {x, i};
        } else if (x->leaf) {
            return {nullptr, 0};
        } else {
            return Search(x->child[i].get(), k);
        }
    }

    void SplitChild(Node* x, std::size_t i) {
        if (!x) {
            return;
        }
        auto y = x->child[i].get();
        if (!y) {
            return;
        }
        assert(!x->isFull() && y->isFull());
        auto z = std::make_unique<Node>();
        z->leaf = y->leaf;
        z->setN(t - 1);
        for (std::size_t j = 0; j < t - 1; j++) {
            z->key[j] = y->key[j + t];
        }
        if (!y->leaf) {
            for (std::size_t j = 0; j < t; j++) {
                z->child[j] = std::move(y->child[j + t]);
            }
        }
        x->setN(x->getN() + 1);
        for (std::size_t j = x->getN(); j >= i && j < x->getN(); j--) {
            x->child[j + 1] = std::move(x->child[j]);
        }
        x->child[i + 1] = std::move(z);
        for (std::size_t j = x->getN(); j >= i && j < x->getN(); j--) {
            x->key[j] = x->key[j - 1];
        }
        x->key[i] = y->key[t - 1];
        y->setN(t - 1);
    }

    void InsertNonFull(Node* x, const T& k) {
        std::size_t i = x->getN();
        if (x->leaf) {
            x->setN(i + 1);
            while (i - 1 < x->getN() && k < x->key[i - 1]) {
                x->key[i] = x->key[i - 1];
                i--;
            }
            x->key[i] = k;
        } else {
            while (i - 1 < x->getN() && k < x->key[i - 1]) {
                i--;
            }
            if (x->child[i]->isFull()) {
                SplitChild(x, i);
                if (k > x->key[i]) {
                    i++;
                }
            }
            InsertNonFull(x->child[i].get(), k);
        }

    }

public:
    BTree() {
        root = std::make_unique<Node>();
    }

    [[nodiscard]] std::pair<const Node*, std::size_t> Search(const T& k) const {
        return Search(root.get(), k);
    }

    void Insert(const T& k) {
        if (root->isFull()) {
            auto s = std::make_unique<Node>();
            s->leaf = false;
            s->setN(0);
            s->child[0] = std::move(root);
            root = std::move(s);
            SplitChild(root.get(), 0);
            InsertNonFull(root.get(), k);
        } else {
            InsertNonFull(root.get(), k);
        }
    }

    friend std::ostream& operator<< (std::ostream& os, const BTree<T, t>& bt) {
        return os << *(bt.root) << '\n';
    }

};

int main() {
    BTree<int, 2> tree;
    tree.Insert(1);
    tree.Insert(2);
    tree.Insert(3);
    tree.Insert(4);
    tree.Insert(5);
    tree.Insert(6);
    tree.Insert(7);
    tree.Insert(8);
    tree.Insert(9);
    tree.Insert(10);

    std::cout << tree;

}