Fix ambiguous initialize_list arguments
This removes the convert-from-arithemtic-scalar constructor of
any_container as it can result in ambiguous calls, as in:
py::array_t<float>({ 1, 2 })
which could be intepreted as either of:
py::array_t<float>(py::array_t<float>(1, 2))
py::array_t<float>(py::detail::any_container({ 1, 2 }))
Removing the convert-from-arithmetic constructor reduces the number of
implicit conversions, avoiding the ambiguity for array and array_t.
This also re-adds the array/array_t constructors taking a scalar
argument for backwards compatibility.
diff --git a/include/pybind11/buffer_info.h b/include/pybind11/buffer_info.h
index e4029b9..e26e063 100644
--- a/include/pybind11/buffer_info.h
+++ b/include/pybind11/buffer_info.h
@@ -36,7 +36,7 @@
}
buffer_info(void *ptr, size_t itemsize, const std::string &format, size_t size)
- : buffer_info(ptr, itemsize, format, 1, size, itemsize) { }
+ : buffer_info(ptr, itemsize, format, 1, {size}, {itemsize}) { }
explicit buffer_info(Py_buffer *view, bool ownview = true)
: buffer_info(view->buf, (size_t) view->itemsize, view->format, (size_t) view->ndim,
diff --git a/include/pybind11/common.h b/include/pybind11/common.h
index d9d9d51..6df39f8 100644
--- a/include/pybind11/common.h
+++ b/include/pybind11/common.h
@@ -690,11 +690,6 @@
template <typename TIn, typename = enable_if_t<std::is_convertible<TIn, T>::value>>
any_container(const std::initializer_list<TIn> &c) : any_container(c.begin(), c.end()) { }
- // Implicit conversion constructor from any arithmetic type (only participates if T is also
- // arithmetic).
- template <typename TIn, typename = enable_if_t<std::is_arithmetic<T>::value && std::is_arithmetic<TIn>::value>>
- any_container(TIn singleton) : v(1, static_cast<T>(singleton)) { }
-
// Avoid copying if given an rvalue vector of the correct type.
any_container(std::vector<T> &&v) : v(std::move(v)) { }
diff --git a/include/pybind11/numpy.h b/include/pybind11/numpy.h
index cbf4c2f..44ea427 100644
--- a/include/pybind11/numpy.h
+++ b/include/pybind11/numpy.h
@@ -458,7 +458,7 @@
forcecast = detail::npy_api::NPY_ARRAY_FORCECAST_
};
- array() : array(0, static_cast<const double *>(nullptr)) {}
+ array() : array({{0}}, static_cast<const double *>(nullptr)) {}
using ShapeContainer = detail::any_container<Py_intptr_t>;
using StridesContainer = detail::any_container<Py_intptr_t>;
@@ -504,12 +504,9 @@
array(const pybind11::dtype &dt, ShapeContainer shape, const void *ptr = nullptr, handle base = handle())
: array(dt, std::move(shape), {}, ptr, base) { }
- // This constructor is only needed to avoid ambiguity with the deprecated (handle, bool)
- // constructor that comes from PYBIND11_OBJECT_CVT; once that is gone, the above constructor can
- // handle it (because ShapeContainer is implicitly constructible from arithmetic types)
- template <typename T, typename = detail::enable_if_t<std::is_arithmetic<T>::value && !std::is_same<bool, T>::value>>
- array(const pybind11::dtype &dt, T count)
- : array(dt, count, nullptr) { }
+ template <typename T, typename = detail::enable_if_t<std::is_integral<T>::value && !std::is_same<bool, T>::value>>
+ array(const pybind11::dtype &dt, T count, const void *ptr = nullptr, handle base = handle())
+ : array(dt, {{count}}, ptr, base) { }
template <typename T>
array(ShapeContainer shape, StridesContainer strides, const T *ptr, handle base = handle())
@@ -519,6 +516,9 @@
array(ShapeContainer shape, const T *ptr, handle base = handle())
: array(std::move(shape), {}, ptr, base) { }
+ template <typename T>
+ explicit array(size_t count, const T *ptr, handle base = handle()) : array({count}, {}, ptr, base) { }
+
explicit array(const buffer_info &info)
: array(pybind11::dtype(info), info.shape, info.strides, info.ptr) { }
@@ -743,6 +743,9 @@
explicit array_t(ShapeContainer shape, const T *ptr = nullptr, handle base = handle())
: array(std::move(shape), ptr, base) { }
+ explicit array_t(size_t count, const T *ptr = nullptr, handle base = handle())
+ : array({count}, {}, ptr, base) { }
+
constexpr size_t itemsize() const {
return sizeof(T);
}
diff --git a/tests/test_numpy_array.cpp b/tests/test_numpy_array.cpp
index 7252547..269f18b 100644
--- a/tests/test_numpy_array.cpp
+++ b/tests/test_numpy_array.cpp
@@ -267,4 +267,10 @@
// Issue #785: Uninformative "Unknown internal error" exception when constructing array from empty object:
sm.def("array_fail_test", []() { return py::array(py::object()); });
sm.def("array_t_fail_test", []() { return py::array_t<double>(py::object()); });
+
+ // Issue (unnumbered; reported in #788): regression: initializer lists can be ambiguous
+ sm.def("array_initializer_list", []() { return py::array_t<float>(1); }); // { 1 } also works, but clang warns about it
+ sm.def("array_initializer_list", []() { return py::array_t<float>({ 1, 2 }); });
+ sm.def("array_initializer_list", []() { return py::array_t<float>({ 1, 2, 3 }); });
+ sm.def("array_initializer_list", []() { return py::array_t<float>({ 1, 2, 3, 4 }); });
});