Skip to content

[BUG]: nb::init uses list-initialization for the class instance #1074

@lahwaacz

Description

@lahwaacz

Problem description

The implementation of nb::init uses list-initialization (i.e. curly braces) when creating an object of the bound class. Therefore the compiler always prefers a user-defined constructor with std::initializer_list parameter (see the reproducible example).

The documentation for nb::init also says that

nb::class_<MyType>(m, "MyType")
    .def(nb::init<const char*, int>());

is syntax sugar for the following lower-level implementation using “placement new”:

nb::class_<MyType>(m, "MyType")
    .def("__init__",
         [](MyType* t, const char* arg0, int arg1) {
             new (t) MyType(arg0, arg1);
         });

but this is not the case – the documentation snippet does not use list-initialization.

I think this is a bug because the expression nb::init<const char*, int>() or even just nb::init<int>() says nothing about list-initialization and it is not possible for the user to avoid it.

Reproducible example code

#include <initializer_list>
#include <iostream>
#include <vector>

#include <nanobind/nanobind.h>

namespace nb = nanobind;

class A
{
    using T = int;
    std::vector<T> data;

public:
    A(std::size_t count)
    : data(count)
    {
        std::cout << "constructor with std::size_t count\n";
    }

    A(std::initializer_list<T> init)
    : data(init)
    {
        std::cout << "constructor with std::initializer_list<T> init\n";
    }
};

NB_MODULE(nanobind_example_ext, m) {
    nb::class_<A>(m, "A")
        .def(nb::init<std::size_t>());
}

Then in Python:

>>> import nanobind_example
>>> nanobind_example.A(1)
constructor with std::initializer_list<T> init

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions