diff --git a/src/polymesh/formats.cc b/src/polymesh/formats.cc
index 1b14e673d34313c49d252017beed7dd27341bd21..bb50e52fc69c7c1f412107ededb630f6caf9d526 100644
--- a/src/polymesh/formats.cc
+++ b/src/polymesh/formats.cc
@@ -48,22 +48,22 @@ bool polymesh::detail::load(const std::string& filename, polymesh::Mesh& m, vert
 }
 
 template <class ScalarT>
-void polymesh::detail::save(const std::string& filename, polymesh::Mesh& m, vertex_attribute<std::array<ScalarT, 3>> const& pos)
+void polymesh::detail::save(std::string const& filename, vertex_attribute<std::array<ScalarT, 3>> const& pos)
 {
     auto ext = filename.substr(filename.rfind('.') + 1);
     std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower);
 
     if (ext == "obj")
     {
-        return write_obj(filename, m, pos);
+        return write_obj(filename, pos);
     }
     else if (ext == "off")
     {
-        return write_off(filename, m, pos);
+        return write_off(filename, pos);
     }
     else if (ext == "stl")
     {
-        return write_stl_binary(filename, m, pos);
+        return write_stl_binary(filename, pos);
     }
     else
     {
@@ -74,5 +74,5 @@ void polymesh::detail::save(const std::string& filename, polymesh::Mesh& m, vert
 template bool polymesh::detail::load<float>(std::string const& filename, Mesh& m, vertex_attribute<std::array<float, 3>>& pos);
 template bool polymesh::detail::load<double>(std::string const& filename, Mesh& m, vertex_attribute<std::array<double, 3>>& pos);
 
-template void polymesh::detail::save<float>(std::string const& filename, Mesh& m, vertex_attribute<std::array<float, 3>> const& pos);
-template void polymesh::detail::save<double>(std::string const& filename, Mesh& m, vertex_attribute<std::array<double, 3>> const& pos);
+template void polymesh::detail::save<float>(std::string const& filename, vertex_attribute<std::array<float, 3>> const& pos);
+template void polymesh::detail::save<double>(std::string const& filename, vertex_attribute<std::array<double, 3>> const& pos);
diff --git a/src/polymesh/formats.hh b/src/polymesh/formats.hh
index 3421afb8626072de3e930d6f1e0408b259383584..0ca8173e73f00da76c5a4e0e516452b2e4cc9791 100644
--- a/src/polymesh/formats.hh
+++ b/src/polymesh/formats.hh
@@ -12,7 +12,7 @@ template <class Pos3>
 bool load(std::string const& filename, Mesh& m, vertex_attribute<Pos3>& pos);
 /// saves a mesh to a file
 template <class Pos3>
-void save(std::string const& filename, Mesh& m, vertex_attribute<Pos3> const& pos);
+void save(std::string const& filename, vertex_attribute<Pos3> const& pos);
 
 template <class Pos3>
 struct load_result
@@ -38,7 +38,7 @@ namespace detail
 template <class ScalarT>
 bool load(std::string const& filename, Mesh& m, vertex_attribute<std::array<ScalarT, 3>>& pos);
 template <class ScalarT>
-void save(std::string const& filename, Mesh& m, vertex_attribute<std::array<ScalarT, 3>> const& pos);
+void save(std::string const& filename, vertex_attribute<std::array<ScalarT, 3>> const& pos);
 } // namespace detail
 
 template <class Pos3>
@@ -64,21 +64,22 @@ bool load(std::string const& filename, Mesh& m, vertex_attribute<Pos3>& pos)
 }
 
 template <class Pos3>
-void save(std::string const& filename, Mesh& m, vertex_attribute<Pos3>& pos)
+void save(std::string const& filename, vertex_attribute<Pos3> const& pos)
 {
     static_assert(sizeof(Pos3) == sizeof(float) * 3 || sizeof(Pos3) == sizeof(double) * 3, "position type must be 3 floats or 3 doubles");
 
+    auto const& m = pos.mesh();
     if (sizeof(Pos3) == sizeof(float) * 3)
     {
-        auto tmp_pos = m.vertices().make_attribute<std::array<float, 3>>();
+        auto tmp_pos = m.vertices().template make_attribute<std::array<float, 3>>();
         std::memcpy(tmp_pos.data(), pos.data(), sizeof(Pos3) * m.vertices().size());
-        detail::save<float>(filename, m, tmp_pos);
+        detail::save<float>(filename, tmp_pos);
     }
     else
     {
-        auto tmp_pos = m.vertices().make_attribute<std::array<double, 3>>();
+        auto tmp_pos = m.vertices().template make_attribute<std::array<double, 3>>();
         std::memcpy(tmp_pos.data(), pos.data(), sizeof(Pos3) * m.vertices().size());
-        detail::save<double>(filename, m, tmp_pos);
+        detail::save<double>(filename, tmp_pos);
     }
 }
 } // namespace polymesh
diff --git a/src/polymesh/formats/obj.cc b/src/polymesh/formats/obj.cc
index 9acdae7ae7b51f726dffd9de23cc8a323b4301eb..b18f00cbcf4c62d2a35b07014dae8beed14b20c5 100644
--- a/src/polymesh/formats/obj.cc
+++ b/src/polymesh/formats/obj.cc
@@ -1,16 +1,16 @@
 #include "obj.hh"
 
-#include <iostream>
 #include <fstream>
+#include <iostream>
 #include <sstream>
 
 namespace polymesh
 {
 template <class ScalarT>
-void write_obj(std::string const& filename, Mesh const& mesh, vertex_attribute<std::array<ScalarT, 3>> const& position)
+void write_obj(std::string const& filename, vertex_attribute<std::array<ScalarT, 3>> const& position)
 {
     obj_writer<ScalarT> obj(filename);
-    obj.write_mesh(mesh, position);
+    obj.write_mesh(position);
 }
 
 template <class ScalarT>
@@ -47,11 +47,12 @@ void obj_writer<ScalarT>::write_object_name(std::string object_name)
 }
 
 template <class ScalarT>
-void obj_writer<ScalarT>::write_mesh(const Mesh& mesh,
-                                     vertex_attribute<std::array<ScalarT, 3>> const& position,
+void obj_writer<ScalarT>::write_mesh(vertex_attribute<std::array<ScalarT, 3>> const& position,
                                      vertex_attribute<std::array<ScalarT, 2>> const* tex_coord,
                                      vertex_attribute<std::array<ScalarT, 3>> const* normal)
 {
+    auto const& mesh = position.mesh();
+
     auto base_v = vertex_idx;
     auto base_t = texture_idx;
     auto base_n = normal_idx;
@@ -102,11 +103,12 @@ void obj_writer<ScalarT>::write_mesh(const Mesh& mesh,
 }
 
 template <class ScalarT>
-void obj_writer<ScalarT>::write_mesh(const Mesh& mesh,
-                                     vertex_attribute<std::array<ScalarT, 4>> const& position,
+void obj_writer<ScalarT>::write_mesh(vertex_attribute<std::array<ScalarT, 4>> const& position,
                                      halfedge_attribute<std::array<ScalarT, 3>> const* tex_coord,
                                      halfedge_attribute<std::array<ScalarT, 3>> const* normal)
 {
+    auto const& mesh = position.mesh();
+
     auto base_v = vertex_idx;
     auto base_t = texture_idx;
     auto base_n = normal_idx;
@@ -375,12 +377,12 @@ void obj_reader<ScalarT>::parse(std::istream& in, Mesh& mesh)
     }
 }
 
-template void write_obj<float>(std::string const& filename, Mesh const& mesh, vertex_attribute<std::array<float, 3>> const& position);
+template void write_obj<float>(std::string const& filename, vertex_attribute<std::array<float, 3>> const& position);
 template bool read_obj<float>(std::string const& filename, Mesh& mesh, vertex_attribute<std::array<float, 3>>& position);
 template struct obj_reader<float>;
 template struct obj_writer<float>;
 
-template void write_obj<double>(std::string const& filename, Mesh const& mesh, vertex_attribute<std::array<double, 3>> const& position);
+template void write_obj<double>(std::string const& filename, vertex_attribute<std::array<double, 3>> const& position);
 template bool read_obj<double>(std::string const& filename, Mesh& mesh, vertex_attribute<std::array<double, 3>>& position);
 template struct obj_reader<double>;
 template struct obj_writer<double>;
diff --git a/src/polymesh/formats/obj.hh b/src/polymesh/formats/obj.hh
index 040edf817f68564170e84641f5c37c771b9a4d89..d61ecbecd08c866fc31fddabaf190b47fe9a5a31 100644
--- a/src/polymesh/formats/obj.hh
+++ b/src/polymesh/formats/obj.hh
@@ -8,7 +8,7 @@
 namespace polymesh
 {
 template <class ScalarT>
-void write_obj(std::string const& filename, Mesh const& mesh, vertex_attribute<std::array<ScalarT, 3>> const& position);
+void write_obj(std::string const& filename, vertex_attribute<std::array<ScalarT, 3>> const& position);
 template <class ScalarT>
 bool read_obj(std::string const& filename, Mesh& mesh, vertex_attribute<std::array<ScalarT, 3>>& position);
 
@@ -20,12 +20,10 @@ struct obj_writer
     ~obj_writer();
 
     void write_object_name(std::string object_name);
-    void write_mesh(Mesh const& mesh,
-                    vertex_attribute<std::array<ScalarT, 4>> const& position,
+    void write_mesh(vertex_attribute<std::array<ScalarT, 4>> const& position,
                     halfedge_attribute<std::array<ScalarT, 3>> const* tex_coord = nullptr,
                     halfedge_attribute<std::array<ScalarT, 3>> const* normal = nullptr);
-    void write_mesh(Mesh const& mesh,
-                    vertex_attribute<std::array<ScalarT, 3>> const& position,
+    void write_mesh(vertex_attribute<std::array<ScalarT, 3>> const& position,
                     vertex_attribute<std::array<ScalarT, 2>> const* tex_coord = nullptr,
                     vertex_attribute<std::array<ScalarT, 3>> const* normal = nullptr);
 
diff --git a/src/polymesh/formats/off.cc b/src/polymesh/formats/off.cc
index aea96918fdc1817ba79753296d2a51d22a326301..7ffe8c4bd2deb69d204b4aa68579fa447d67e2bb 100644
--- a/src/polymesh/formats/off.cc
+++ b/src/polymesh/formats/off.cc
@@ -1,21 +1,23 @@
 #include "off.hh"
 
-#include <iostream>
 #include <fstream>
+#include <iostream>
 #include <sstream>
 
 namespace polymesh
 {
 template <class ScalarT>
-void write_off(const std::string& filename, const Mesh& mesh, const vertex_attribute<std::array<ScalarT, 3>>& position)
+void write_off(const std::string& filename, vertex_attribute<std::array<ScalarT, 3>> const& position)
 {
     std::ofstream file(filename);
-    write_off(file, mesh, position);
+    write_off(file, position);
 }
 
 template <class ScalarT>
-void write_off(std::ostream& out, const Mesh& mesh, const vertex_attribute<std::array<ScalarT, 3>>& position)
+void write_off(std::ostream& out, vertex_attribute<std::array<ScalarT, 3>> const& position)
 {
+    auto const& mesh = position.mesh();
+
     out << "OFF\n";
     out << mesh.vertices().size() << " " << mesh.faces().size() << " " << mesh.edges().size() << "\n";
 
@@ -95,13 +97,13 @@ bool read_off(std::istream& input, Mesh& mesh, vertex_attribute<std::array<Scala
     return non_manifold == 0;
 }
 
-template void write_off<float>(std::string const& filename, Mesh const& mesh, vertex_attribute<std::array<float, 3>> const& position);
-template void write_off<float>(std::ostream& out, Mesh const& mesh, vertex_attribute<std::array<float, 3>> const& position);
+template void write_off<float>(std::string const& filename, vertex_attribute<std::array<float, 3>> const& position);
+template void write_off<float>(std::ostream& out, vertex_attribute<std::array<float, 3>> const& position);
 template bool read_off<float>(std::string const& filename, Mesh& mesh, vertex_attribute<std::array<float, 3>>& position);
 template bool read_off<float>(std::istream& input, Mesh& mesh, vertex_attribute<std::array<float, 3>>& position);
 
-template void write_off<double>(std::string const& filename, Mesh const& mesh, vertex_attribute<std::array<double, 3>> const& position);
-template void write_off<double>(std::ostream& out, Mesh const& mesh, vertex_attribute<std::array<double, 3>> const& position);
+template void write_off<double>(std::string const& filename, vertex_attribute<std::array<double, 3>> const& position);
+template void write_off<double>(std::ostream& out, vertex_attribute<std::array<double, 3>> const& position);
 template bool read_off<double>(std::string const& filename, Mesh& mesh, vertex_attribute<std::array<double, 3>>& position);
 template bool read_off<double>(std::istream& input, Mesh& mesh, vertex_attribute<std::array<double, 3>>& position);
 } // namespace polymesh
diff --git a/src/polymesh/formats/off.hh b/src/polymesh/formats/off.hh
index 421ad861429556a74e3c856ab7c37afae52cf0f3..7847a862392c8cb88134817936905f6a40b49073 100644
--- a/src/polymesh/formats/off.hh
+++ b/src/polymesh/formats/off.hh
@@ -9,9 +9,9 @@
 namespace polymesh
 {
 template <class ScalarT>
-void write_off(std::string const& filename, Mesh const& mesh, vertex_attribute<std::array<ScalarT, 3>> const& position);
+void write_off(std::string const& filename, vertex_attribute<std::array<ScalarT, 3>> const& position);
 template <class ScalarT>
-void write_off(std::ostream& out, Mesh const& mesh, vertex_attribute<std::array<ScalarT, 3>> const& position);
+void write_off(std::ostream& out, vertex_attribute<std::array<ScalarT, 3>> const& position);
 template <class ScalarT>
 bool read_off(std::string const& filename, Mesh& mesh, vertex_attribute<std::array<ScalarT, 3>>& position);
 template <class ScalarT>
diff --git a/src/polymesh/formats/stl.cc b/src/polymesh/formats/stl.cc
index c0296f3b9be71223f7f9f5ef3d5f5c1546930dd2..1d2cf6f1339c0e7465f09bbb4e5d621b77390bfd 100644
--- a/src/polymesh/formats/stl.cc
+++ b/src/polymesh/formats/stl.cc
@@ -21,18 +21,17 @@
 namespace polymesh
 {
 template <class ScalarT>
-void write_stl_binary(const std::string& filename,
-                      const Mesh& mesh,
-                      const vertex_attribute<std::array<ScalarT, 3>>& position,
-                      face_attribute<std::array<ScalarT, 3>> const* normals)
+void write_stl_binary(std::string const& filename, vertex_attribute<std::array<ScalarT, 3>> const& position, face_attribute<std::array<ScalarT, 3>> const* normals)
 {
     std::ofstream file(filename, std::ios_base::binary);
-    write_stl_binary(file, mesh, position, normals);
+    write_stl_binary(file, position, normals);
 }
 
 template <class ScalarT>
-void write_stl_binary(std::ostream& out, const Mesh& mesh, const vertex_attribute<std::array<ScalarT, 3>>& position, face_attribute<std::array<ScalarT, 3>> const* normals)
+void write_stl_binary(std::ostream& out, vertex_attribute<std::array<ScalarT, 3>> const& position, face_attribute<std::array<ScalarT, 3>> const* normals)
 {
+    auto const& mesh = position.mesh();
+
     char header[80] = {};
     uint32_t n_triangles = mesh.faces().size();
 
@@ -261,26 +260,18 @@ bool is_ascii_stl(std::istream& input)
 }
 
 template void write_stl_binary<float>(std::string const& filename,
-                                      Mesh const& mesh,
-                                      vertex_attribute<std::array<float, 3>> const& position,
-                                      face_attribute<std::array<float, 3>> const* normals);
-template void write_stl_binary<float>(std::ostream& out,
-                                      Mesh const& mesh,
                                       vertex_attribute<std::array<float, 3>> const& position,
                                       face_attribute<std::array<float, 3>> const* normals);
+template void write_stl_binary<float>(std::ostream& out, vertex_attribute<std::array<float, 3>> const& position, face_attribute<std::array<float, 3>> const* normals);
 template bool read_stl<float>(std::string const& filename, Mesh& mesh, vertex_attribute<std::array<float, 3>>& position, face_attribute<std::array<float, 3>>* normals);
 template bool read_stl<float>(std::istream& input, Mesh& mesh, vertex_attribute<std::array<float, 3>>& position, face_attribute<std::array<float, 3>>* normals);
 template bool read_stl_binary<float>(std::istream& input, Mesh& mesh, vertex_attribute<std::array<float, 3>>& position, face_attribute<std::array<float, 3>>* normals);
 template bool read_stl_ascii<float>(std::istream& input, Mesh& mesh, vertex_attribute<std::array<float, 3>>& position, face_attribute<std::array<float, 3>>* normals);
 
 template void write_stl_binary<double>(std::string const& filename,
-                                       Mesh const& mesh,
-                                       vertex_attribute<std::array<double, 3>> const& position,
-                                       face_attribute<std::array<double, 3>> const* normals);
-template void write_stl_binary<double>(std::ostream& out,
-                                       Mesh const& mesh,
                                        vertex_attribute<std::array<double, 3>> const& position,
                                        face_attribute<std::array<double, 3>> const* normals);
+template void write_stl_binary<double>(std::ostream& out, vertex_attribute<std::array<double, 3>> const& position, face_attribute<std::array<double, 3>> const* normals);
 template bool read_stl<double>(std::string const& filename, Mesh& mesh, vertex_attribute<std::array<double, 3>>& position, face_attribute<std::array<double, 3>>* normals);
 template bool read_stl<double>(std::istream& input, Mesh& mesh, vertex_attribute<std::array<double, 3>>& position, face_attribute<std::array<double, 3>>* normals);
 template bool read_stl_binary<double>(std::istream& input, Mesh& mesh, vertex_attribute<std::array<double, 3>>& position, face_attribute<std::array<double, 3>>* normals);
diff --git a/src/polymesh/formats/stl.hh b/src/polymesh/formats/stl.hh
index 2a5c4016fe2364e0c48f8762729d47ecba943bf3..347318acd92d7d758c71637a0fb1f45ffba84f7a 100644
--- a/src/polymesh/formats/stl.hh
+++ b/src/polymesh/formats/stl.hh
@@ -10,14 +10,10 @@ namespace polymesh
 {
 template <class ScalarT>
 void write_stl_binary(std::string const& filename,
-                      Mesh const& mesh,
                       vertex_attribute<std::array<ScalarT, 3>> const& position,
                       face_attribute<std::array<ScalarT, 3>> const* normals = nullptr);
 template <class ScalarT>
-void write_stl_binary(std::ostream& out,
-                      Mesh const& mesh,
-                      vertex_attribute<std::array<ScalarT, 3>> const& position,
-                      face_attribute<std::array<ScalarT, 3>> const* normals = nullptr);
+void write_stl_binary(std::ostream& out, vertex_attribute<std::array<ScalarT, 3>> const& position, face_attribute<std::array<ScalarT, 3>> const* normals = nullptr);
 template <class ScalarT>
 bool read_stl(std::string const& filename, Mesh& mesh, vertex_attribute<std::array<ScalarT, 3>>& position, face_attribute<std::array<ScalarT, 3>>* normals = nullptr);
 template <class ScalarT>