C++: operator<< kann nicht kompiliert werden
Ich codiere einen B-Baum in C++, aber wenn ich überlade operator<<, kann er nicht kompiliert werden.
Mein Code:
#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;
}
Dies ist die erste Zeile der Beschwerde von meinem Compiler (ich werde nicht die vollständige Fehlerausgabe veröffentlichen, da sie mehr als 200 Zeilen enthält):
/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';
| ~~~^~~~~~~~~~~~~
btis BTree<T, t>, bt.rootis std::unique_ptr<BTree<T, t>::Node>, also denke ich, dass der Dereferenzierungsoperator mit übereinstimmen sollte operator<<(std::ostream& os, const typename BTree<T, t>::Node& node), aber mein Compiler ist immer noch nicht glücklich.
Wie kann ich das beheben? Danke im Voraus.
Antworten
operator<<für die innere Klasse Nodeist als Vorlage definiert, aber die Vorlagenparameter können aufgrund des nicht abgeleiteten Kontexts nicht abgeleitet werden .
In den folgenden Fällen nehmen die Typen, Vorlagen und Nicht-Typ-Werte, die zum Zusammensetzen von P verwendet werden, nicht an der Ableitung von Vorlagenargumenten teil, sondern verwenden stattdessen die Vorlagenargumente, die entweder an anderer Stelle abgeleitet oder explizit angegeben wurden. Wenn ein Vorlagenparameter nur in nicht abgeleiteten Kontexten verwendet und nicht explizit angegeben wird, schlägt die Ableitung von Vorlagenargumenten fehl.
- Der verschachtelte Namensbezeichner (alles links vom Bereichsauflösungsoperator
::) eines Typs, der mit einer qualifizierten ID angegeben wurde :
Sie können es als Nicht-Vorlage definieren und es in der Klassendefinition definieren. Z.B
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;
}
...
};
...
};
Wow, C++-Templates sind so obskur und dumm.
Arbeitscode:
#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;
}