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.
This commit is contained in:
Jason Rhinelander 2017-04-28 11:06:16 -04:00
parent 2761f78f11
commit 51d18aa252
4 changed files with 17 additions and 13 deletions

View File

@ -36,7 +36,7 @@ struct buffer_info {
}
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,

View File

@ -690,11 +690,6 @@ public:
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)) { }

View File

@ -458,7 +458,7 @@ public:
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 @@ public:
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 @@ public:
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 @@ public:
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);
}

View File

@ -267,4 +267,10 @@ test_initializer numpy_array([](py::module &m) {
// 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 }); });
});