#include <iostream>
template <typename T>
struct node {
T value;
node const* prev;
constexpr node(const T& value, node const* prev = nullptr)
: value{value}, prev{prev} {}
constexpr node push_front(const T& value) const {
return node(value, this);
}
};
struct Something {
node<int> n;
constexpr Something(const int i) : n{node<int>(i)} {}
constexpr Something(const node<int>& n) : n{n} {}
};
constexpr void print(const Something& s) {
bool first = true;
for (const node<int>* i = &s.n; i != nullptr; i = i->prev) {
if (first) {
first = false;
} else {
std::cout << ", ";
}
std::cout << i->value;
}
}
constexpr Something recursive_case(Something& s, const unsigned int i) {
Something result(s.n.push_front(i % 10));
auto j = i / 10;
return j != 0 ? recursive_case(result, j) : result;
}
constexpr Something base_case(const unsigned int i) {
Something result(i % 10);
auto j = i / 10;
return j != 0 ? recursive_case(result, j) : result;
}
int main() { print(base_case(21)); }
我有一个如上所示的递归函数(base_case
和recursive_case
)。我从这个链接获得了node
对象的想法:https://gist.github.com/dabrahams/1457531#file-constexpr_demo-cpp-L66,我修改它以满足我的需要。我上面的问题是我遇到了分段错误。
感谢。
修改(一个或多个):
很抱歉没有提前尝试调试器。这是输出:
$ lldb ./ww ~/scratch/ww
(lldb) target create "./ww"
Current executable set to './ww' (x86_64).
(lldb) run
Process 32909 launched: './ww' (x86_64)
Process 32909 stopped
* thread #1: tid = 0x4d4e8e, 0x000000010000109b ww`print(s=0x00007fff5fbfec80) + 91 at ww.cpp:32, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
frame #0: 0x000000010000109b ww`print(s=0x00007fff5fbfec80) + 91 at ww.cpp:32
29 } else {
30 std::cout << ", ";
31 }
-> 32 std::cout << i->value;
33 }
34 }
35
(lldb)
我会尝试使用new
或智能指针,但根据我的阅读,它们不能在constexpr
函数中使用。
我尝试使用new
和智能指针。
对于new
,我收到此错误:
ww.cpp:19:15: error: constexpr constructor never produces a constant expression [-Winvalid-constexpr]
constexpr Something(const int i) : n{new node<int>(i)} {}
^
ww.cpp:19:42: note: subexpression not valid in a constant expression
constexpr Something(const int i) : n{new node<int>(i)} {}
^
对于unique_ptr
,我收到此错误:
ww.cpp:26:11: note: non-constexpr constructor 'unique_ptr' cannot be used in a constant expression
: n{std::unique_ptr<node<int>, deleter<node<int>>>(new node<int>(i))} {}
我进一步研究了它,我认为使用C ++模板可以解决这个问题。我只需要一种方法来捕获递归的中间结果,例如某种编译时列表然后颠倒顺序。
答案 0 :(得分:3)
这是一个有趣的问题,但正如Joachim Pileborg在评论中所说的那样,在调试器中执行程序会让你知道崩溃的原因。
首先是诊断:
print
功能时一切正常:s
包含一个包含前一个节点的节点,并且全部(s.prev->prev == nullptr
)std::cout << i->value;
它被破坏之后:s.prev->prev != nullptr
当指令不应该改变它时!当它在调用函数时断开,它闻起来像是一个悬挂的指针或引用...
现在解释:
在你的递归调用中,所有内容(Something
和node
都被分配为局部变量并作为引用传递给递归调用。当调用时,一切都很好,所有内容都分配在一个调用函数。但是当你返回时,只有Something
及其node
被更正:所有后续节点都被分配为现在结束函数中的局部变量,并且(调用result
返回值) result.prev
是一个悬垂的指针。
使用悬空指针是未定义的行为,可以使用SIGSEGV。
TL / DR:您在递归调用中将链接列表的节点分配为局部变量,并以悬空指针结束,因此未定义的行为有望立即导致崩溃。
警告:未定义的行为可以在所有测试中起作用,并在生产后期中断
可能的修复:由于类不能包含自身的实例,因此不能在链表中使用return by value,并且必须使用动态分配。因此,您必须实现显式析构函数或使用智能指针。
以上主要是对坠机事件的解释。可以使用智能指针修复它,但不能再使用constexpr
。如果以这种方式修改struct node
:
template <typename T>
struct node {
T value;
std::shared_ptr<node const> prev;
node(const T& value, node const* prev = nullptr)
: value{value} {
this->prev = (prev == nullptr)
? std::shared_ptr<struct node const>(nullptr)
: std::make_shared<struct node const>(*prev);
}
node push_front(const T& value) const {
return node(value, this);
}
};
它可以通过复制安全返回,因为shared_ptr
确保您永远不会获得悬空指针。但确实构造函数不再是constexpr
,所以只有print
仍然是一个......
但这是有道理的。当recursive_case
重新建立一个链表时,我无法想象一种方法来制作一个constexpr。首先可以分配一个node
的数组(因为这里每个数字需要node
),然后链接那些现有的节点。但如果它们在递归函数中被分配为局部变量,则无法避免悬空问题,如果它们是动态分配的,则不能是constexpr
答案 1 :(得分:1)
添加
constexpr node(const node& i_rhs)
: value(i_rhs.value)
, prev(i_rhs.prev == nullptr ? nullptr : new node(*i_rhs.prev))
{}
constexpr node(const node&& i_rhs)
: value(i_rhs.value)
, prev(i_rhs.prev)
{}
为我工作。
据我了解,整个情况如下:
此修复程序通过执行对象的深层复制来解决此问题。但是此修复导致内存泄漏:构造函数中分配的内存永远不会被释放。这可以通过使用智能指针或显式实现析构函数来解决。