I started experimenting with CRTP and ran into a very interesting problem that has been keeping me awake for two days now.
Using CRTP I declare 3 structures: Column
, Header
and Footer
, which are inherited from Widget
.
The Column
structure takes the other widgets in the constructor and stores them in std::tuple, then in the Render()
method, calls the Render()
method of the widgets that are stored in std::tuple
.
In main()
you can see that the Header
widget's title field is set, but during debugging the Render()
method of the Column
widget I noticed that the title field looks at uninitialized memory, so the program ends up with a segfault.
The code uses standard c++20
.
Who can suggest what the problem is? Thanks in advance!
#include <iostream>
#include <tuple>
#include <string>
template<typename T>
struct Widget {
void Render() {
static_cast<T*>(this)->Render();
}
};
template<typename ...Args>
struct Column : public Widget<Column<Args...>> {
Column(Widget<Args>... args)
: children(std::forward<Widget<Args>>(args)...)
{}
void Render() {
std::cout << "Column {" << std::endl;
std::apply([](auto&... widgets){
auto render = [](auto& widget){
widget.Render();
};
(render(widgets), ...);
}, children);
std::cout << "}" << std::endl;
}
private:
std::tuple<Widget<Args>...> children;
};
struct Header : public Widget<Header> {
void Render() {
std::cout << "Header { title=" << this->title << " }" << std::endl;
}
std::string title;
};
struct Footer : public Widget<Footer> {
void Render() {
std::cout << "Footer {}" << std::endl;
}
};
template<typename T>
void AplicationRun(Widget<T> widget) {
widget.Render();
}
int main() {
auto app = Column{
Header{
.title = "Title"
},
Column{},
Footer{},
};
AplicationRun(app);
return 0;
}
I tried using move sematic in the Column
constructor but that didn't help.