奇怪的重复模板模式 - 不能创建超过 1 个派生 class?
Curiously Recurring Template Pattern - Can't create more than 1 derived class?
我无法使用这种模式,因为只有一个我创建的派生 classes 被实例化。使用 g++ 和 MSVS 检查。具体来说,只有我定义的第一个派生 class 被创建。编译器不会发出任何类型的警告。下面提供了完整的代码。
#include <iostream>
static int nodes = 0;
class TreeNode {
private:
int m_id;
public:
TreeNode() :
m_id(++nodes)
{}
TreeNode(int id) :
m_id(id)
{
++nodes;
}
TreeNode* left;
TreeNode* right;
int getId() const {
return m_id;
}
};
template<typename T>
//typename std::enable_if<std::is_base_of<TreeParser, T>::value>::type
class TreeParser {
protected:
TreeParser() {
++parsers;
}
public:
static uint32_t parsers;
void preorderTraversal(TreeNode* node) {
if (node != nullptr) {
processNode(node);
preorderTraversal(node->left);
preorderTraversal(node->right);
}
}
virtual ~TreeParser() = default;
void processNode(TreeNode* node) { // 2, 3. the generic algorithm is customized by derived classes
static_cast<T*>(this)->processNode(node); // depending on the client's demand - the right function will be called
}
};
template<class T>
uint32_t TreeParser<T>::parsers = 0;
class SpecializedTreeParser1 : public TreeParser<SpecializedTreeParser1> // 1. is-a relationship
{
public:
explicit SpecializedTreeParser1() :
TreeParser()
{}
void processNode(TreeNode* node) {
std::cout << "Customized (derived - SpecializedTreeParser1) processNode(node) - "
"id=" << node->getId() << '\n';
}
};
class SpecializedTreeParser2 : public TreeParser<SpecializedTreeParser2> // 1. is-a relationship
{
public:
explicit SpecializedTreeParser2() :
TreeParser()
{}
void processNode(TreeNode* node) {
std::cout << "Customized (derived - SpecializedTreeParser2) processNode(node) - "
"id=" << node->getId() << '\n';
}
};
int main()
{
TreeNode root;
TreeNode leftChild;
TreeNode rightChild;
root.left = &leftChild;
root.right = &rightChild;
std::cout << "Root id: " << root.getId() << '\n';
std::cout << "Left child id: " << leftChild.getId() << '\n';
std::cout << "Right child id: " << rightChild.getId() << '\n';
SpecializedTreeParser1 _1;
_1.preorderTraversal(&root);
SpecializedTreeParser2 _2;
_2.preorderTraversal(&root);
}
输出为:
Root id: 1
Left child id: 2
Right child id: 3
Customized (derived - SpecializedTreeParser1) preorderTraversal() - id=1
Customized (derived - SpecializedTreeParser1) preorderTraversal() - id=2
Customized (derived - SpecializedTreeParser1) preorderTraversal() - id=1963060099 // what is that?
为什么我不能实例化第二个派生的class?
我没记错,你的程序有未定义的行为。 leftChild
和 rightChild
的 left
和 right
指针未初始化,因此一旦 preorderTraversal()
到达那里,您的程序就会崩溃。这也是为什么你最后得到奇怪的 id
的原因:它是从某个随机内存位置读取的……
要解决此问题,请确保 TreeNode
的 left
和 right
成员始终初始化为 nullptr
,正如您的代码的其余部分所期望的那样:
class TreeNode {
…
TreeNode* left = nullptr;
TreeNode* right = nullptr;
…
};
The problem is SpecializedTreeParser2 _2;
. TreeParser leftChild
and
rightChild
are initialized fine, their id
is printed. Check the output
哦,未定义行为的乐趣...
不,TreeParser leftChild
和 rightChild
未正确初始化,问题不在于 SpecializedTreeParser2 _2
。
问题是 TreeNode::left;
和 TreeNode* right;
未初始化使用。这导致你的程序有未定义的行为,而未定义行为的美是整个程序有未定义的行为,而不仅仅是使用未初始化的变量。这意味着虽然 SpecializedTreeParser1 _1
看起来还可以,但实际上并非如此。您看到的输出是属于未定义行为的行为。分析它的来源是没有意义的,为什么 _1
似乎有效而 _2
无效。这是未定义的行为。修复它,不要试图理解为什么会出现这种特定行为。
让我用一个更短的例子来告诉你我的意思:
int main()
{
int a = 24;
std::cout << a << std::endl;
int b; // uninitialized
std::cout << b << std::endl; // <-- this line makes the WHOLE program to have UB
}
现在上述程序的可能行为是什么?
print "24"
print <garbage>
可以合理预期
print "24"
print "0"
也可以
print "24"
<crash>
也不足为奇
print <garbage>
print "24"
也有可能……等什么?是的!!程序的未定义性质不必在 std::cout << b << std::endl
行表现出来。程序的整个执行是未定义的。 任何事情 都可能发生!
所有这些也是可能的:
<crash>
print "0"
print "0"
print "24"
print "24"
print "24"
print "0"
<crash>
print "Here be dragons"
我无法使用这种模式,因为只有一个我创建的派生 classes 被实例化。使用 g++ 和 MSVS 检查。具体来说,只有我定义的第一个派生 class 被创建。编译器不会发出任何类型的警告。下面提供了完整的代码。
#include <iostream>
static int nodes = 0;
class TreeNode {
private:
int m_id;
public:
TreeNode() :
m_id(++nodes)
{}
TreeNode(int id) :
m_id(id)
{
++nodes;
}
TreeNode* left;
TreeNode* right;
int getId() const {
return m_id;
}
};
template<typename T>
//typename std::enable_if<std::is_base_of<TreeParser, T>::value>::type
class TreeParser {
protected:
TreeParser() {
++parsers;
}
public:
static uint32_t parsers;
void preorderTraversal(TreeNode* node) {
if (node != nullptr) {
processNode(node);
preorderTraversal(node->left);
preorderTraversal(node->right);
}
}
virtual ~TreeParser() = default;
void processNode(TreeNode* node) { // 2, 3. the generic algorithm is customized by derived classes
static_cast<T*>(this)->processNode(node); // depending on the client's demand - the right function will be called
}
};
template<class T>
uint32_t TreeParser<T>::parsers = 0;
class SpecializedTreeParser1 : public TreeParser<SpecializedTreeParser1> // 1. is-a relationship
{
public:
explicit SpecializedTreeParser1() :
TreeParser()
{}
void processNode(TreeNode* node) {
std::cout << "Customized (derived - SpecializedTreeParser1) processNode(node) - "
"id=" << node->getId() << '\n';
}
};
class SpecializedTreeParser2 : public TreeParser<SpecializedTreeParser2> // 1. is-a relationship
{
public:
explicit SpecializedTreeParser2() :
TreeParser()
{}
void processNode(TreeNode* node) {
std::cout << "Customized (derived - SpecializedTreeParser2) processNode(node) - "
"id=" << node->getId() << '\n';
}
};
int main()
{
TreeNode root;
TreeNode leftChild;
TreeNode rightChild;
root.left = &leftChild;
root.right = &rightChild;
std::cout << "Root id: " << root.getId() << '\n';
std::cout << "Left child id: " << leftChild.getId() << '\n';
std::cout << "Right child id: " << rightChild.getId() << '\n';
SpecializedTreeParser1 _1;
_1.preorderTraversal(&root);
SpecializedTreeParser2 _2;
_2.preorderTraversal(&root);
}
输出为:
Root id: 1
Left child id: 2
Right child id: 3
Customized (derived - SpecializedTreeParser1) preorderTraversal() - id=1
Customized (derived - SpecializedTreeParser1) preorderTraversal() - id=2
Customized (derived - SpecializedTreeParser1) preorderTraversal() - id=1963060099 // what is that?
为什么我不能实例化第二个派生的class?
我没记错,你的程序有未定义的行为。 leftChild
和 rightChild
的 left
和 right
指针未初始化,因此一旦 preorderTraversal()
到达那里,您的程序就会崩溃。这也是为什么你最后得到奇怪的 id
的原因:它是从某个随机内存位置读取的……
要解决此问题,请确保 TreeNode
的 left
和 right
成员始终初始化为 nullptr
,正如您的代码的其余部分所期望的那样:
class TreeNode {
…
TreeNode* left = nullptr;
TreeNode* right = nullptr;
…
};
The problem is
SpecializedTreeParser2 _2;
.TreeParser leftChild
andrightChild
are initialized fine, theirid
is printed. Check the output
哦,未定义行为的乐趣...
不,TreeParser leftChild
和 rightChild
未正确初始化,问题不在于 SpecializedTreeParser2 _2
。
问题是 TreeNode::left;
和 TreeNode* right;
未初始化使用。这导致你的程序有未定义的行为,而未定义行为的美是整个程序有未定义的行为,而不仅仅是使用未初始化的变量。这意味着虽然 SpecializedTreeParser1 _1
看起来还可以,但实际上并非如此。您看到的输出是属于未定义行为的行为。分析它的来源是没有意义的,为什么 _1
似乎有效而 _2
无效。这是未定义的行为。修复它,不要试图理解为什么会出现这种特定行为。
让我用一个更短的例子来告诉你我的意思:
int main()
{
int a = 24;
std::cout << a << std::endl;
int b; // uninitialized
std::cout << b << std::endl; // <-- this line makes the WHOLE program to have UB
}
现在上述程序的可能行为是什么?
print "24"
print <garbage>
可以合理预期
print "24"
print "0"
也可以
print "24"
<crash>
也不足为奇
print <garbage>
print "24"
也有可能……等什么?是的!!程序的未定义性质不必在 std::cout << b << std::endl
行表现出来。程序的整个执行是未定义的。 任何事情 都可能发生!
所有这些也是可能的:
<crash>
print "0"
print "0"
print "24"
print "24"
print "24"
print "0"
<crash>
print "Here be dragons"