Repository: ChaiScript/ChaiScript (2026-04-11T14:54:46Z)

Issue:
A std::vector of std::unique_ptrs can't be added
 * Compiler Used: VS2013 (MSVC 12) with ChaiScript v5.8.6
 * Operating System: Windows 10
 * Architecture (ARM/x86/32bit/64bit/etc):  64bit
 

### Expected Behavior 

Being able to successfully compile the following line:

```cpp
struct Foo {};
chai->add(chaiscript::bootstrap::standard_library::vector_type<std::vector<std::unique_ptr<Foo>>>("Foo"));
```

### Actual Behavior 

The compiler error is

```Error  1  error C2280:
std::unique_ptr<Foo,std::default_delete<_Ty>>::unique_ptr(const
std::unique_ptr<_Ty,std::default_delete<_Ty>> &)' : attempting to reference a
deleted function  c:\program files (x86)\microsoft visual studio
12.0\vc\include\vector  1708

```

### Minimal Example to Reproduce Behavior

```cpp
#include <chaiscript/chaiscript.hpp>
#include <chaiscript/chaiscript_stdlib.hpp>
#include <vector>


int main() {
  chaiscript::ChaiScript chai;
   struct Foo {};
   chai.add(chaiscript::bootstrap::standard_library::vector_type<std::vector<std::unique_ptr<Foo>>>("Foo"));
}
```

### Workaround

I went through and identified the bits that caused the compiler to generate a call to the copy constructor of the unique_ptr and commented them out, and got it to compile. ~~Haven't tested actually using the pointers in chai much, but operator[] doesn't work on the unique_ptr vectors with my hack, so its a weak workaround.~~ Using the updated code where I consume the module I create with the hacky extracted code, I'm able to use vectors of unique_ptrs, so that's good.

 [Copy and pasted from here](https://github.com/ChaiScript/ChaiScript/blob/50ca037da95399a3f88b416288e8d111caa24e32/include/chaiscript/dispatchkit/bootstrap_stl.hpp#L487) got turned into this:

```cpp
template <typename VectorType>
chaiscript::ModulePtr register_vector_of_unique_ptr(const std::string& type)
{

    using namespace chaiscript;
    ModulePtr m = std::make_shared<Module>();
    m->add(user_type<VectorType>(), type);

    typedef VectorType::reference(VectorType::*frontptr)();
    typedef VectorType::const_reference(VectorType::*constfrontptr)() const;

    m->add(fun(static_cast<frontptr>(&VectorType::front)), "front");
    m->add(fun(static_cast<constfrontptr>(&VectorType::front)), "front");


    //bootstrap::standard_library::back_insertion_sequence_type<VectorType>(type, m); //breaks unique_ptrs
    //bootstrap::standard_library::sequence_type<VectorType>(type, m); //breaks unique_ptrs
    bootstrap::standard_library::random_access_container_type<VectorType>(type, m);
    bootstrap::standard_library::container_type<VectorType>(type, m);
    bootstrap::standard_library::default_constructible_type<VectorType>(type, m);
    //bootstrap::standard_library::assignable_type<VectorType>(type, m); //breaks unique_ptrs
    bootstrap::standard_library::input_range_type<VectorType>(type, m);

    if (typeid(VectorType) == typeid(std::vector<Boxed_Value>))
    {
        m->eval(R"(
                    def Vector::`==`(Vector rhs) {
                       if ( rhs.size() != this.size() ) {
                         return false;
                       } else {
                         auto r1 = range(this);
                         auto r2 = range(rhs);
                         while (!r1.empty())
                         {
                           if (!eq(r1.front(), r2.front()))
                           {
                             return false;
                           }
                           r1.pop_front();
                           r2.pop_front();
                         }
                         true;
                       }
                   } )"
                   );
    }

  return m;
}

//elsewhere, make sure to consume the module ptr
chai_instance->add(register_vector_of_unique_ptr<CustomVecOfUniqPtrs>(chai_instance));
```




Discussion:
I just updated the original question because I realized I wasn't adding the module containing the vector of unique pointers. So the initial compile issue remains, but the workaround there isn't too bad.
@leftibot this should be fixable with an `if constexpr(std::is_copy_constructible<>)` test in the appropriate places. Please fix.
Neat, thanks!



Patch:
diff --git a/include/chaiscript/dispatchkit/bootstrap_stl.hpp b/include/chaiscript/dispatchkit/bootstrap_stl.hpp
index f56304d7..5bbc17e0 100644
--- a/include/chaiscript/dispatchkit/bootstrap_stl.hpp
+++ b/include/chaiscript/dispatchkit/bootstrap_stl.hpp
@@ -18,6 +18,7 @@
 #include <functional>
 #include <memory>
 #include <stdexcept>
+#include <type_traits>
 #include <typeinfo>
 #include <vector>
 
@@ -170,18 +171,22 @@ namespace chaiscript::bootstrap::standard_library {
   /// http://www.sgi.com/tech/stl/Assignable.html
   template<typename ContainerType>
   void assignable_type(const std::string &type, Module &m) {
-    copy_constructor<ContainerType>(type, m);
-    operators::assign<ContainerType>(m);
+    if constexpr (std::is_copy_constructible_v<typename ContainerType::value_type>) {
+      copy_constructor<ContainerType>(type, m);
+      operators::assign<ContainerType>(m);
+    }
   }
 
   /// Add container resize concept to the given ContainerType
   /// http://www.cplusplus.com/reference/stl/
   template<typename ContainerType>
   void resizable_type(const std::string & /*type*/, Module &m) {
-    m.add(fun([](ContainerType *a, typename ContainerType::size_type n, const typename ContainerType::value_type &val) {
-            return a->resize(n, val);
-          }),
-          "resize");
+    if constexpr (std::is_copy_constructible_v<typename ContainerType::value_type>) {
+      m.add(fun([](ContainerType *a, typename ContainerType::size_type n, const typename ContainerType::value_type &val) {
+              return a->resize(n, val);
+            }),
+            "resize");
+    }
     m.add(fun([](ContainerType *a, typename ContainerType::size_type n) { return a->resize(n); }), "resize");
   }
 
@@ -213,13 +218,15 @@ namespace chaiscript::bootstrap::standard_library {
   /// http://www.sgi.com/tech/stl/Sequence.html
   template<typename ContainerType>
   void sequence_type(const std::string & /*type*/, Module &m) {
-    m.add(fun(&detail::insert_at<ContainerType>), []() -> std::string {
-      if (typeid(typename ContainerType::value_type) == typeid(Boxed_Value)) {
-        return "insert_ref_at";
-      } else {
-        return "insert_at";
-      }
-    }());
+    if constexpr (std::is_copy_constructible_v<typename ContainerType::value_type>) {
+      m.add(fun(&detail::insert_at<ContainerType>), []() -> std::string {
+        if (typeid(typename ContainerType::value_type) == typeid(Boxed_Value)) {
+          return "insert_ref_at";
+        } else {
+          return "insert_at";
+        }
+      }());
+    }
 
     m.add(fun(&detail::erase_at<ContainerType>), "erase_at");
   }
@@ -245,27 +252,29 @@ namespace chaiscript::bootstrap::standard_library {
           }),
           "back");
 
-    using push_back = void (ContainerType::*)(const typename ContainerType::value_type &);
-    m.add(fun(static_cast<push_back>(&ContainerType::push_back)), [&]() -> std::string {
-      if (typeid(typename ContainerType::value_type) == typeid(Boxed_Value)) {
-        m.eval("# Pushes the second value onto the container while making a clone of the value\n"
-               "def push_back("
-               + type
-               + " container, x)\n"
-                 "{ \n"
-                 "  if (x.is_var_return_value()) {\n"
-                 "    x.reset_var_return_value() \n"
-                 "    container.push_back_ref(x) \n"
-                 "  } else { \n"
-                 "    container.push_back_ref(clone(x)); \n"
-                 "  }\n"
-                 "} \n");
-
-        return "push_back_ref";
-      } else {
-        return "push_back";
-      }
-    }());
+    if constexpr (std::is_copy_constructible_v<typename ContainerType::value_type>) {
+      using push_back = void (ContainerType::*)(const typename ContainerType::value_type &);
+      m.add(fun(static_cast<push_back>(&ContainerType::push_back)), [&]() -> std::string {
+        if (typeid(typename ContainerType::value_type) == typeid(Boxed_Value)) {
+          m.eval("# Pushes the second value onto the container while making a clone of the value\n"
+                 "def push_back("
+                 + type
+                 + " container, x)\n"
+                   "{ \n"
+                   "  if (x.is_var_return_value()) {\n"
+                   "    x.reset_var_return_value() \n"
+                   "    container.push_back_ref(x) \n"
+                   "  } else { \n"
+                   "    container.push_back_ref(clone(x)); \n"
+                   "  }\n"
+                   "} \n");
+
+          return "push_back_ref";
+        } else {
+          return "push_back";
+        }
+      }());
+    }
 
     m.add(fun(&ContainerType::pop_back), "pop_back");
   }
@@ -295,25 +304,27 @@ namespace chaiscript::bootstrap::standard_library {
           }),
           "front");
 
-    m.add(fun(static_cast<push_ptr>(&ContainerType::push_front)), [&]() -> std::string {
-      if (typeid(typename ContainerType::value_type) == typeid(Boxed_Value)) {
-        m.eval("# Pushes the second value onto the front of container while making a clone of the value\n"
-               "def push_front("
-               + type
-               + " container, x)\n"
-                 "{ \n"
-                 "  if (x.is_var_return_value()) {\n"
-                 "    x.reset_var_return_value() \n"
-                 "    container.push_front_ref(x) \n"
-                 "  } else { \n"
-                 "    container.push_front_ref(clone(x)); \n"
-                 "  }\n"
-                 "} \n");
-        return "push_front_ref";
-      } else {
-        return "push_front";
-      }
-    }());
+    if constexpr (std::is_copy_constructible_v<typename ContainerType::value_type>) {
+      m.add(fun(static_cast<push_ptr>(&ContainerType::push_front)), [&]() -> std::string {
+        if (typeid(typename ContainerType::value_type) == typeid(Boxed_Value)) {
+          m.eval("# Pushes the second value onto the front of container while making a clone of the value\n"
+                 "def push_front("
+                 + type
+                 + " container, x)\n"
+                   "{ \n"
+                   "  if (x.is_var_return_value()) {\n"
+                   "    x.reset_var_return_value() \n"
+                   "    container.push_front_ref(x) \n"
+                   "  } else { \n"
+                   "    container.push_front_ref(clone(x)); \n"
+                   "  }\n"
+                   "} \n");
+          return "push_front_ref";
+        } else {
+          return "push_front";
+        }
+      }());
+    }
 
     m.add(fun(static_cast<pop_ptr>(&ContainerType::pop_front)), "pop_front");
   }


Test patch:
diff --git a/unittests/compiled_tests.cpp b/unittests/compiled_tests.cpp
index c4094da1..0a2d252b 100644
--- a/unittests/compiled_tests.cpp
+++ b/unittests/compiled_tests.cpp
@@ -1437,6 +1437,31 @@ TEST_CASE("Issue #421 - Switch with type_conversion does not compare destroyed o
   })") == 0);
 }
 
+// Issue #524: A std::vector of std::unique_ptrs can't be added
+// vector_type should compile with non-copyable value types by
+// skipping copy-dependent operations via if constexpr.
+struct Issue524_Foo {
+  int value = 42;
+};
+
+TEST_CASE("Issue #524 - vector of unique_ptr can be registered") {
+  using VecType = std::vector<std::unique_ptr<Issue524_Foo>>;
+
+  chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser());
+
+  // This should compile and not throw - previously failed to compile
+  // because vector_type tried to instantiate copy constructor, assignment,
+  // push_back(const T&), insert_at(const T&), and resize(n, const T&)
+  // for the non-copyable std::unique_ptr<Issue524_Foo>.
+  chaiscript::ModulePtr m = std::make_shared<chaiscript::Module>();
+  chaiscript::bootstrap::standard_library::vector_type<VecType>("UniqueVec", *m);
+  CHECK_NOTHROW(chai.add(m));
+
+  // Verify basic operations still work
+  CHECK(chai.eval<size_t>("var v = UniqueVec(); v.size()") == 0);
+  CHECK(chai.eval<bool>("var v2 = UniqueVec(); v2.empty()") == true);
+}
+
 // Issue #625: function_less_than comparator must satisfy strict-weak ordering.
 // Registering overloaded functions with different arities triggered a
 // std::stable_sort assertion on macOS 15.2 (hardened libc++) because the