Reputation: 1526
I want to have a linked list like lisp
cons lists. I want to do this on compile time in C++, utilizing templates and constexpr
as much as I can. Here is my attempt, I think you'll see immediately what is wrong.
#include <iostream>
namespace chops {
template <typename E1, typename E2>
struct pair {
constexpr pair()
: _car{}, _cdr{}
{}
constexpr pair(const E1 &car)
:_car{car}, _cdr{}
{}
constexpr pair(const E1 &car, const E2 &cdr)
:_car{car}, _cdr{cdr}
{}
constexpr auto car() const{
return _car;
}
constexpr auto cdr() const{
return _cdr;
}
const E1 _car;
const E2 _cdr;
};
template <typename E1, typename E2>
std::ostream& operator<<(std::ostream& str,
pair<E1, E2> p){
str << p.car() << " " << p.cdr();
str << "\n";
return str;
}
template <typename Head, typename Tail>
class list{
public:
constexpr list():p{}
{}
constexpr list(Head h)
:p{h}
{}
constexpr list(Head h, Tail t)
:p{h, t}
{}
constexpr auto append(Head h) const{
return list<Head, decltype(p)>{h, p};
}
friend std::ostream& operator<<(std::ostream& str,
list<Head, Tail> l){
str << l.p;
return str;
}
private:
pair<Head, Tail> p;
};
}
int main()
{
//constexpr const chops::pair p1{3, 4};
//constexpr const chops::pair<int, decltype(p1)> p2{2, p1};
//constexpr const chops::pair<int, decltype(p2)> p3(1, p2);
constexpr chops::list<int, int> l1{6, 5};
auto l2 = l1.append(4);
auto l3 = l2.append(3);
auto l4 = l3.append(2);
auto l5 = l4.append(1);
std::cout << l5;
}
You can see that my list
is just a wrapper around pair actually and I am losing the type safety. l1
has type list<int, int>
and others are just nested pairs. This is not good, I want all of them to have the type list<T>
. How can I handle the type Tail
without getting it into template parameter of list
class. Things should stay constexpr, of course.
Upvotes: 0
Views: 371
Reputation: 275800
add a copy ctor:
constexpr list(list const& l):p{l.p}
{}
then:
template<class NewHead>
constexpr auto prepend(NewHead h) const{
return list<NewHead, list>{h, *this};
}
(I renamed the function; it pushes in front, not at the tail).
To append you need some pattern matching. It is best done as a free function.
template<class Head, class Tail>
constexpr auto append( Head h, Tail t ) {
return list<Head, Tail>(h, t);
}
and within list
:
template<class NewTail>
friend constexpr auto append( list h, NewTail t ) {
return append( car(h.p), append( cdr(h.p), t ) );
}
that might work if I got the overloading right.
Use would look like:
auto l2 = append(l1,4);
Upvotes: 2