diff --git a/example/brunel/brunel_miniapp.cpp b/example/brunel/brunel_miniapp.cpp
index ea6ca86d0821b016a77aaacc01aeeadf148c6143..37a1da79c0131f035973ad399c40358781779525 100644
--- a/example/brunel/brunel_miniapp.cpp
+++ b/example/brunel/brunel_miniapp.cpp
@@ -120,8 +120,8 @@ public:
         return cell;
     }
 
-    std::vector<event_generator_ptr> event_generators(cell_gid_type gid) const override {
-        std::vector<arb::event_generator_ptr> gens;
+    std::vector<event_generator> event_generators(cell_gid_type gid) const override {
+        std::vector<arb::event_generator> gens;
 
         std::mt19937_64 G;
         G.seed(gid + seed_);
@@ -131,12 +131,7 @@ public:
         time_type t0 = 0;
         cell_member_type target{gid, 0};
 
-        gens.push_back(arb::make_event_generator<pgen>(
-                        target,
-                        weight_ext_,
-                        G,
-                        t0,
-                        lambda_));
+        gens.emplace_back(pgen(target, weight_ext_, G, t0, lambda_));
         return gens;
     }
 
diff --git a/example/generators/event_gen.cpp b/example/generators/event_gen.cpp
index df69cd2e8599d735fe2d94b32fcd05d5dac27a9f..41293bea9f14951e3fa3cf728f52fb83a313268c 100644
--- a/example/generators/event_gen.cpp
+++ b/example/generators/event_gen.cpp
@@ -72,7 +72,7 @@ public:
     }
 
     // Return two generators attached to the one cell.
-    std::vector<arb::event_generator_ptr> event_generators(cell_gid_type gid) const override {
+    std::vector<arb::event_generator> event_generators(cell_gid_type gid) const override {
         EXPECTS(gid==0); // There is only one cell in the model
 
         using RNG = std::mt19937_64;
@@ -88,21 +88,19 @@ public:
         double w_i = -0.005;
 
         // Make two event generators.
-        std::vector<arb::event_generator_ptr> gens;
+        std::vector<arb::event_generator> gens;
 
         // Add excitatory generator
         gens.push_back(
-            arb::make_event_generator<pgen>(
-                cell_member_type{0,0}, // Target synapse (gid, local_id).
-                w_e,                   // Weight of events to deliver
-                RNG(29562872),         // Random number generator to use
-                t0,                    // Events start being delivered from this time
-                lambda_e));            // Expected frequency (events per ms)
+            pgen(cell_member_type{0,0}, // Target synapse (gid, local_id).
+                 w_e,                   // Weight of events to deliver
+                 RNG(29562872),         // Random number generator to use
+                 t0,                    // Events start being delivered from this time
+                 lambda_e));            // Expected frequency (events per ms)
 
         // Add inhibitory generator
-        gens.push_back(
-            arb::make_event_generator<pgen>(
-                cell_member_type{0,0}, w_i, RNG(86543891), t0, lambda_i));
+        gens.emplace_back(
+            pgen(cell_member_type{0,0}, w_i, RNG(86543891), t0, lambda_i));
 
         return gens;
     }
diff --git a/example/generators/readme.md b/example/generators/readme.md
index d910aadcea678e192046a49acd6a0f61a2f1c299..a18a853ab09637ea739696be01e062ef97c680e2 100644
--- a/example/generators/readme.md
+++ b/example/generators/readme.md
@@ -87,7 +87,7 @@ To add the event generators to the synapse, we implement the `recipe::event_gene
 The implementation of this with hard-coded frequencies and weights is:
 
 ```C++
-    std::vector<arb::event_generator_ptr> event_generators(cell_gid_type gid) const override {
+    std::vector<arb::event_generator> event_generators(cell_gid_type gid) const override {
         // The type of random number generator to use.
         using RNG = std::mt19937_64;
 
@@ -104,47 +104,33 @@ The implementation of this with hard-coded frequencies and weights is:
         double w_i = -0.005;
 
         // Make two event generators.
-        std::vector<arb::event_generator_ptr> gens;
+        std::vector<arb::event_generator> gens;
 
         // Add excitatory generator
-        gens.push_back(
-            arb::make_event_generator<pgen>(
-                cell_member_type{0,0}, // Target synapse (gid, local_id).
-                w_e,                   // Weight of events to deliver
-                RNG(29562872),         // Random number generator to use
-                t0,                    // Events start being delivered from this time
-                lambda_e));            // Expected frequency (events per ms)
+        gens.emplace_back(
+            pgen(cell_member_type{0,0}, // Target synapse (gid, local_id).
+                 w_e,                   // Weight of events to deliver
+                 RNG(29562872),         // Random number generator to use
+                 t0,                    // Events start being delivered from this time
+                 lambda_e));            // Expected frequency (events per ms)
 
         // Add inhibitory generator
-        gens.push_back(
-            arb::make_event_generator<pgen>(
-                cell_member_type{0,0}, w_i, RNG(86543891), t0, lambda_i));
+        gens.emplace_back(
+            pgen(cell_member_type{0,0}, w_i, RNG(86543891), t0, lambda_i));
 
         return gens;
     }
 ```
 
-The `recipe::event_generators(gid)` method returns a vector of `event_generator_ptr` that are attached to the cell with `gid`.
-
-The `event_generator_ptr` is an alias for a `unique_ptr` wrapped around an `event_generator`.
-
-```C++
-using event_generator_ptr = std::unique_ptr<event_generator>;
-```
+The `recipe::event_generators(gid)` method returns a vector of `event_generator`s that are attached to the cell with `gid`.
 
 In the implementation, an empty vector is created, and the generators are created and `push_back`ed into the vector one after the other.
 
-The helper function `make_event_generator` is used to simplify the process of creating an event generator and wrapping it in a `unique_ptr`.
-It has the same semantics as [http://en.cppreference.com/w/cpp/memory/unique_ptr/make_unique](`std::make_unique`):
-
-* It takes as a template parameter the specialized type of `event_generator`, in this case `pgen`.
-* Then it takes as arguments the arguments to pass to the constructor of `pgen`.
-
-Of the arguments passed to the Poisson event generators, the random number generator state require further explanation.
+Of the arguments used to construct the Poisson event generator `pgen`, the random number generator state require further explanation.
 Each Poisson generator has its own private random number generator state.
 The initial random number state is provided on construction.
-For a real world model, the state should have a seed that is some reproducable hash of `gid` and the generator id, to ensure reproducable random streams.
-For this simple example, we use hard coded seeds to initialize the random number state.
+For a real world model, the state should have a seed that is a hash of `gid` and the generator id, to ensure reproducable random streams.
+For this simple example, we use hard-coded seeds to initialize the random number state.
 
 ### Sampling Voltages
 
diff --git a/src/event_generator.hpp b/src/event_generator.hpp
index 2a461198c9f599e297ac4d5449659250de62fbbb..60819ef951544a76bd7dd3d5834e6212dc65d785 100644
--- a/src/event_generator.hpp
+++ b/src/event_generator.hpp
@@ -11,38 +11,122 @@
 
 namespace arb {
 
+inline
+postsynaptic_spike_event terminal_pse() {
+    return postsynaptic_spike_event{cell_member_type{0,0}, max_time, 0};
+}
+
 // An event_generator generates a sequence of events to be delivered to a cell.
 // The sequence of events is always in ascending order, i.e. each event will be
 // greater than the event that proceded it, where events are ordered by:
 //  - delivery time;
 //  - then target id for events with the same delivery time;
 //  - then weight for events with the same delivery time and target.
-struct event_generator {
-    // Return the next event in the stream.
-    // Returns the same event if called multiple times without calling pop().
-    virtual postsynaptic_spike_event next() = 0;
+class event_generator {
+public:
+    //
+    // copy, move and constructor interface
+    //
+
+    event_generator(): event_generator(dummy_generator()) {}
+
+    template <typename Impl>
+    event_generator(Impl&& impl):
+        impl_(new wrap<Impl>(std::forward<Impl>(impl)))
+    {}
+
+    event_generator(event_generator&& other) = default;
+    event_generator& operator=(event_generator&& other) = default;
+
+    event_generator(const event_generator& other):
+        impl_(other.impl_->clone())
+    {}
+
+    event_generator& operator=(const event_generator& other) {
+        impl_ = other.impl_->clone();
+        return *this;
+    }
+
+    //
+    // event generator interface
+    //
+
+    // Get the current event in the stream.
+    // Does not modify the state of the stream, i.e. multiple calls to
+    // next() will return the same event in the absence of calls to pop(),
+    // advance() or reset().
+    postsynaptic_spike_event next() {
+        return impl_->next();
+    }
 
     // Move the generator to the next event in the stream.
-    virtual void pop() = 0;
+    void pop() {
+        impl_->pop();
+    }
 
     // Reset the generator to the same state that it had on construction.
-    virtual void reset() = 0;
+    void reset() {
+        impl_->reset();
+    }
 
     // Update state of the generator such that the event returned by next() is
     // the first event with delivery time >= t.
-    virtual void advance(time_type t) = 0;
+    void advance(time_type t) {
+        return impl_->advance(t);
+    }
 
-    virtual ~event_generator() {};
-};
+private:
+    struct interface {
+        virtual postsynaptic_spike_event next() = 0;
+        virtual void pop() = 0;
+        virtual void advance(time_type t) = 0;
+        virtual void reset() = 0;
+        virtual std::unique_ptr<interface> clone() = 0;
+        virtual ~interface() {}
+    };
+
+    std::unique_ptr<interface> impl_;
+
+    template <typename Impl>
+    struct wrap: interface {
+        explicit wrap(const Impl& impl): wrapped(impl) {}
+        explicit wrap(Impl&& impl): wrapped(std::move(impl)) {}
+
+        postsynaptic_spike_event next() override {
+            return wrapped.next();
+        }
 
-inline
-postsynaptic_spike_event terminal_pse() {
-    return postsynaptic_spike_event{cell_member_type{0,0}, max_time, 0};
-}
+        void pop() override {
+            return wrapped.pop();
+        }
+
+        void advance(time_type t) override {
+            return wrapped.advance(t);
+        }
+
+        void reset() override {
+            wrapped.reset();
+        }
+
+        std::unique_ptr<interface> clone() override {
+            return std::unique_ptr<interface>(new wrap<Impl>(wrapped));
+        }
+
+        Impl wrapped;
+    };
+
+    struct dummy_generator {
+        postsynaptic_spike_event next() { return terminal_pse(); }
+        void pop() {}
+        void reset() {}
+        void advance(time_type t) {};
+    };
+
+};
 
 // Generator that feeds events that are specified with a vector.
 // Makes a copy of the input sequence of events.
-struct vector_backed_generator: public event_generator {
+struct vector_backed_generator {
     using pse = postsynaptic_spike_event;
     vector_backed_generator(pse_vector events):
         events_(std::move(events)),
@@ -53,21 +137,21 @@ struct vector_backed_generator: public event_generator {
         }
     }
 
-    postsynaptic_spike_event next() override {
+    postsynaptic_spike_event next() {
         return it_==events_.end()? terminal_pse(): *it_;
     }
 
-    void pop() override {
+    void pop() {
         if (it_!=events_.end()) {
             ++it_;
         }
     }
 
-    void reset() override {
+    void reset() {
         it_ = events_.begin();
     }
 
-    void advance(time_type t) override {
+    void advance(time_type t) {
         it_ = std::lower_bound(events_.begin(), events_.end(), t, event_time_less());
     }
 
@@ -81,7 +165,7 @@ private:
 // Care must be taken to avoid lifetime issues, to ensure that the generator
 // does not outlive the sequence.
 template <typename Seq>
-struct seq_generator: public event_generator {
+struct seq_generator {
     using pse = postsynaptic_spike_event;
     seq_generator(Seq& events):
         events_(events),
@@ -90,21 +174,21 @@ struct seq_generator: public event_generator {
         EXPECTS(std::is_sorted(events_.begin(), events_.end()));
     }
 
-    postsynaptic_spike_event next() override {
+    postsynaptic_spike_event next() {
         return it_==events_.end()? terminal_pse(): *it_;
     }
 
-    void pop() override {
+    void pop() {
         if (it_!=events_.end()) {
             ++it_;
         }
     }
 
-    void reset() override {
+    void reset() {
         it_ = events_.begin();
     }
 
-    void advance(time_type t) override {
+    void advance(time_type t) {
         it_ = std::lower_bound(events_.begin(), events_.end(), t, event_time_less());
     }
 
@@ -117,7 +201,7 @@ private:
 // Generates a set of regularly spaced events:
 //  * with delivery times t=t_start+n*dt, ∀ t ∈ [t_start, t_stop)
 //  * with a set target and weight
-struct regular_generator: public event_generator {
+struct regular_generator {
     using pse = postsynaptic_spike_event;
 
     regular_generator(cell_member_type target,
@@ -133,18 +217,18 @@ struct regular_generator: public event_generator {
         t_stop_(tstop)
     {}
 
-    postsynaptic_spike_event next() override {
+    postsynaptic_spike_event next() {
         const auto t = time();
         return t<t_stop_?
             postsynaptic_spike_event{target_, t, weight_}:
             terminal_pse();
     }
 
-    void pop() override {
+    void pop() {
         ++step_;
     }
 
-    void advance(time_type t0) override {
+    void advance(time_type t0) {
         t0 = std::max(t0, t_start_);
         step_ = (t0-t_start_)/dt_;
 
@@ -159,7 +243,7 @@ struct regular_generator: public event_generator {
         }
     }
 
-    void reset() override {
+    void reset() {
         step_ = 0;
     }
 
@@ -179,7 +263,7 @@ private:
 // Generates a stream of events at times described by a Poisson point process
 // with rate_per_ms spikes per ms.
 template <typename RandomNumberEngine>
-struct poisson_generator: public event_generator {
+struct poisson_generator {
     using pse = postsynaptic_spike_event;
 
     poisson_generator(cell_member_type target,
@@ -198,23 +282,23 @@ struct poisson_generator: public event_generator {
         reset();
     }
 
-    postsynaptic_spike_event next() override {
+    postsynaptic_spike_event next() {
         return next_<t_stop_?
             postsynaptic_spike_event{target_, next_, weight_}:
             terminal_pse();
     }
 
-    void pop() override {
+    void pop() {
         next_ += exp_(rng_);
     }
 
-    void advance(time_type t0) override {
+    void advance(time_type t0) {
         while (next_<t0) {
             pop();
         }
     }
 
-    void reset() override {
+    void reset() {
         rng_ = reset_state_;
         next_ = t_start_;
         pop();
@@ -229,15 +313,7 @@ private:
     const time_type t_start_;
     const time_type t_stop_;
     time_type next_;
-
 };
 
-using event_generator_ptr = std::unique_ptr<event_generator>;
-
-template <typename T, typename... Args>
-event_generator_ptr make_event_generator(Args&&... args) {
-    return event_generator_ptr(new T(std::forward<Args>(args)...));
-}
-
 } // namespace arb
 
diff --git a/src/merge_events.cpp b/src/merge_events.cpp
index e5cda151b3e4be3bbfc64e1cc7c1891a626fdfb7..c839dd071a8c6c6d3c2ad74855517db190a722ce 100644
--- a/src/merge_events.cpp
+++ b/src/merge_events.cpp
@@ -30,7 +30,7 @@ namespace impl {
 // unsigned is used for storing the index, because if drawing events from more
 // event generators than can be counted using an unsigned a complete redesign
 // will be needed.
-tourney_tree::tourney_tree(std::vector<event_generator_ptr>& input):
+tourney_tree::tourney_tree(std::vector<event_generator>& input):
     input_(input),
     n_lanes_(input_.size())
 {
@@ -47,7 +47,7 @@ tourney_tree::tourney_tree(std::vector<event_generator_ptr>& input):
     // Set the leaf nodes
     for (auto i=0u; i<leaves_; ++i) {
         heap_[leaf(i)] = i<n_lanes_?
-            key_val(i, input[i]->next()):
+            key_val(i, input[i].next()):
             key_val(i, terminal_pse()); // null leaf node
     }
     // Walk the tree to initialize the non-leaf nodes
@@ -80,9 +80,9 @@ void tourney_tree::pop() {
     unsigned lane = id(0);
     unsigned i = leaf(lane);
     // draw the next event from the input lane
-    input_[lane]->pop();
+    input_[lane].pop();
     // place event the leaf node for this lane
-    event(i) = input_[lane]->next();
+    event(i) = input_[lane].next();
 
     // re-heapify the tree with a single walk from leaf to root
     while ((i=parent(i))) {
@@ -144,7 +144,7 @@ unsigned tourney_tree::next_power_2(unsigned x) const {
 
 void merge_events(time_type t0, time_type t1,
                   const pse_vector& lc, pse_vector& events,
-                  std::vector<event_generator_ptr>& generators,
+                  std::vector<event_generator>& generators,
                   pse_vector& lf)
 {
     using std::distance;
@@ -167,12 +167,12 @@ void merge_events(time_type t0, time_type t1,
         EXPECTS(generators.size()>2u);
 
         // Make an event generator with all the events in events.
-        generators[0] = make_event_generator<seq_generator<pse_vector>>(events);
+        generators[0] = seq_generator<pse_vector>(events);
 
         // Make an event generator with all the events in lc with time >= t0
         auto lc_it = lower_bound(lc.begin(), lc.end(), t0, event_time_less());
         auto lc_range = util::make_range(lc_it, lc.end());
-        generators[1] = make_event_generator<seq_generator<decltype(lc_range)>>(lc_range);
+        generators[1] = seq_generator<decltype(lc_range)>(lc_range);
 
         // Perform k-way merge of all events in events, lc and the generators
         // that are due to be delivered in the interval [t₀, t₁)
@@ -190,6 +190,9 @@ void merge_events(time_type t0, time_type t1,
         const auto n = m + distance(lc_it, lc.end()) + distance(ev_it, events.end());
         lf.resize(n);
         std::merge(ev_it, events.end(), lc_it, lc.end(), lf.begin()+m);
+
+        // clear the generators associated with temporary event sequences
+        generators[0] = generators[1] = event_generator();
     }
     else {
         // Handle the case where the cell has no event generators: only events
diff --git a/src/merge_events.hpp b/src/merge_events.hpp
index 9976d6cc655e2306610dcd47fc78049df7a96424..df1596ccd9be123688d3760dd50b2ef782042f5c 100644
--- a/src/merge_events.hpp
+++ b/src/merge_events.hpp
@@ -40,7 +40,7 @@ void merge_events(time_type t0,
                   time_type t1,
                   const pse_vector& lc,
                   pse_vector& pending_events,
-                  std::vector<event_generator_ptr>& generators,
+                  std::vector<event_generator>& generators,
                   pse_vector& lf);
 
 namespace impl {
@@ -51,7 +51,7 @@ namespace impl {
         using key_val = std::pair<unsigned, postsynaptic_spike_event>;
 
     public:
-        tourney_tree(std::vector<event_generator_ptr>& input);
+        tourney_tree(std::vector<event_generator>& input);
         bool empty() const;
         bool empty(time_type t) const;
         postsynaptic_spike_event head() const;
@@ -73,7 +73,7 @@ namespace impl {
         unsigned next_power_2(unsigned x) const;
 
         std::vector<key_val> heap_;
-        const std::vector<event_generator_ptr>& input_;
+        std::vector<event_generator>& input_;
         unsigned leaves_;
         unsigned nodes_;
         unsigned n_lanes_;
diff --git a/src/model.cpp b/src/model.cpp
index 36e8c8e1c033ec9bb6f2f69905427a797e6b2ba2..3e0dd8700f085f8ec311bf199f9b21cee6c8e26a 100644
--- a/src/model.cpp
+++ b/src/model.cpp
@@ -85,10 +85,8 @@ void model::reset() {
     // Reset all event generators, and advance to t_.
     for (auto& lane: event_generators_) {
         for (auto& gen: lane) {
-            if (gen) {
-                gen->reset();
-                gen->advance(t_);
-            }
+            gen.reset();
+            gen.advance(t_);
         }
     }
 
diff --git a/src/model.hpp b/src/model.hpp
index acac870322b451c2fa3bb1995f8c699d66330873..1ace8489034f2bab325a0b77a8d522efb50aaacd 100644
--- a/src/model.hpp
+++ b/src/model.hpp
@@ -76,7 +76,7 @@ private:
     std::vector<cell_group_ptr> cell_groups_;
 
     // one set of event_generators for each local cell
-    std::vector<std::vector<event_generator_ptr>> event_generators_;
+    std::vector<std::vector<event_generator>> event_generators_;
 
     using local_spike_store_type = thread_private_spike_store;
     util::double_buffer<local_spike_store_type> local_spikes_;
diff --git a/src/recipe.hpp b/src/recipe.hpp
index a8981dbc6191e0ff17f1385b4a4bd17922cbbd5f..0a193548ea637e72e4001e2543b965099b9d7aa9 100644
--- a/src/recipe.hpp
+++ b/src/recipe.hpp
@@ -64,7 +64,7 @@ public:
     virtual cell_size_type num_targets(cell_gid_type) const { return 0; }
     virtual cell_size_type num_probes(cell_gid_type)  const { return 0; }
 
-    virtual std::vector<event_generator_ptr> event_generators(cell_gid_type) const {
+    virtual std::vector<event_generator> event_generators(cell_gid_type) const {
         return {};
     }
     virtual std::vector<cell_connection> connections_on(cell_gid_type) const {
diff --git a/tests/global_communication/test_domain_decomposition.cpp b/tests/global_communication/test_domain_decomposition.cpp
index d6cef200ee086c16be70b392dd80e2804e5a5e01..5fc97e5aca52a5060b590fffa0567921454bc417 100644
--- a/tests/global_communication/test_domain_decomposition.cpp
+++ b/tests/global_communication/test_domain_decomposition.cpp
@@ -54,7 +54,7 @@ namespace {
             return {};
         }
 
-        std::vector<event_generator_ptr> event_generators(cell_gid_type) const override {
+        std::vector<event_generator> event_generators(cell_gid_type) const override {
             return {};
         }
 
diff --git a/tests/unit/test_event_generators.cpp b/tests/unit/test_event_generators.cpp
index 76b6c6c30976e6ee0dd4bac2747bf5b0c123b546..34afb0588216a0bbce0d7965fbdfa10fbdc8c52f 100644
--- a/tests/unit/test_event_generators.cpp
+++ b/tests/unit/test_event_generators.cpp
@@ -108,7 +108,7 @@ TEST(event_generator, regular_rounding) {
     time_type int_len = 5*dt;
     time_type t1 = t0 + int_len;
     time_type t2 = t1 + int_len;
-    auto gen = regular_generator(target, weight, t0, dt);
+    event_generator gen = regular_generator(target, weight, t0, dt);
 
     // Take the interval I_a: t ∈ [t0, t2)
     // And the two sub-interavls
@@ -147,7 +147,7 @@ TEST(event_generators, seq) {
         return pse_vector(in.begin()+b, in.begin()+e);
     };
 
-    seq_generator<pse_vector> gen(in);
+    event_generator gen = seq_generator<pse_vector>(in);
 
     // Test pop, next and reset.
     for (auto e: in) {
diff --git a/tests/unit/test_lif_cell_group.cpp b/tests/unit/test_lif_cell_group.cpp
index 8cc7f921a22892defacbbf555b34c29eaadf7401..9793533031243974fe4db6a811474fa9b215d31f 100644
--- a/tests/unit/test_lif_cell_group.cpp
+++ b/tests/unit/test_lif_cell_group.cpp
@@ -81,7 +81,7 @@ public:
     probe_info get_probe(cell_member_type probe_id) const override {
         return {};
     }
-    std::vector<event_generator_ptr> event_generators(cell_gid_type) const override {
+    std::vector<event_generator> event_generators(cell_gid_type) const override {
         return {};
     }
 
@@ -134,7 +134,7 @@ public:
     probe_info get_probe(cell_member_type probe_id) const override {
         return {};
     }
-    std::vector<event_generator_ptr> event_generators(cell_gid_type) const override {
+    std::vector<event_generator> event_generators(cell_gid_type) const override {
         return {};
     }
 
diff --git a/tests/unit/test_merge_events.cpp b/tests/unit/test_merge_events.cpp
index 481f8cf9379813192b812415b3e1e0297937f9a6..d7b8d0a24b60a302c5b1d39f64ece28e106dacaf 100644
--- a/tests/unit/test_merge_events.cpp
+++ b/tests/unit/test_merge_events.cpp
@@ -6,7 +6,7 @@
 
 using namespace arb;
 
-std::vector<event_generator_ptr> empty_gens;
+std::vector<event_generator> empty_gens;
 
 // Test the trivial case of merging empty sets
 TEST(merge_events, empty)
@@ -127,10 +127,9 @@ TEST(merge_events, X)
         {{3, 0}, 26, 4},
     };
 
-    std::vector<event_generator_ptr> generators(2);
-    generators.push_back(
-        make_event_generator<regular_generator>
-        (cell_member_type{4,2}, 42.f, t0, 5));
+    std::vector<event_generator> generators(2);
+    generators.emplace_back(
+        regular_generator(cell_member_type{4,2}, 42.f, t0, 5));
 
     merge_events(t0, t1, lc, events, generators, lf);
 
@@ -170,9 +169,9 @@ TEST(merge_events, tourney_seq)
         {{0, 0}, 5.5, 5},
     };
 
-    std::vector<event_generator_ptr> generators;
-    generators.push_back(make_event_generator<seq_generator<pse_vector>>(g1));
-    generators.push_back(make_event_generator<seq_generator<pse_vector>>(g2));
+    std::vector<event_generator> generators;
+    generators.emplace_back(seq_generator<pse_vector>(g1));
+    generators.emplace_back(seq_generator<pse_vector>(g2));
     impl::tourney_tree tree(generators);
 
     pse_vector lf;
@@ -201,30 +200,28 @@ TEST(merge_events, tourney_poisson)
     time_type t0 = 0;
     time_type lambda = 10; // expected: tfinal*lambda=1000 events per generator
 
-    std::vector<event_generator_ptr> generators;
+    std::vector<event_generator> generators;
     for (auto i=0u; i<ngen; ++i) {
         cell_member_type tgt{0, i};
         float weight = i;
         // the first and last generators have the same seed to test that sorting
         // of events with the same time but different weights works properly.
         rndgen G(i%(ngen-1));
-        generators.push_back(
-            make_event_generator<
-                poisson_generator<std::mt19937_64>>
-                (tgt, weight, G, t0, lambda));
+        generators.emplace_back(
+                poisson_generator<std::mt19937_64>(tgt, weight, G, t0, lambda));
     }
 
     // manually generate the expected output
     pse_vector expected;
     for (auto& gen: generators) {
         // Push all events before tfinal in gen to the expected values.
-        while (gen->next().time<tfinal) {
-            expected.push_back(gen->next());
-            gen->pop();
+        while (gen.next().time<tfinal) {
+            expected.push_back(gen.next());
+            gen.pop();
         }
         // Reset the generator so that it is ready to generate the same
         // events again for the tournament tree test.
-        gen->reset();
+        gen.reset();
     }
     // Manually sort the expected events.
     util::sort(expected);