Skip to content

Comments

fix: bind noexcept and ref-qualified methods from unregistered base classes#5992

Open
Skylion007 wants to merge 11 commits intopybind:masterfrom
Skylion007:skylion007/cpp17-claude-noexcept-2026-02-20
Open

fix: bind noexcept and ref-qualified methods from unregistered base classes#5992
Skylion007 wants to merge 11 commits intopybind:masterfrom
Skylion007:skylion007/cpp17-claude-noexcept-2026-02-20

Conversation

@Skylion007
Copy link
Collaborator

@Skylion007 Skylion007 commented Feb 20, 2026

Description

Fix issue #2234 . Fix overloading to accept noexcept function types too and strip them. Mostly authored by ClaudeCode with a lot of human intervention.

fix: support noexcept and ref-qualified methods from unregistered base classes (issue #2234)

Problem

In C++17, noexcept became part of the function type (P0012R1). This means
int (Base::*)() const noexcept and int (Base::*)() const are distinct
types
. When a derived class inherits methods from an unregistered base,
method_adaptor must recast the member function pointer from Base::* to
Derived::*. Without explicit template specializations for noexcept (and
ref-qualified) variants, the generic F&& fallback was selected, leaving the
pointer typed as Base::* — causing pybind11 to look up Base as self,
which fails at runtime for unregistered base classes.

Similarly, cpp_function lacked constructors for noexcept, & noexcept,
const noexcept, const & noexcept, &&, const &&, && noexcept, and
const && noexcept member function pointer types, so those overloads silently
fell through to the generic lambda constructor.

Solution

include/pybind11/pybind11.h

  • Add cpp_function constructors for all missing qualifier combinations:
    • &&, const && (unconditional): use std::move(*c).*f in the lambda
    • noexcept, const noexcept, & noexcept, const & noexcept,
      && noexcept, const && noexcept (under #ifdef __cpp_noexcept_function_type)
  • Add detail::rebind_member_ptr<Derived, T> type trait mapping
    Return (Base::*)(Args...) qualifiers to Return (Derived::*)(Args...) qualifiers
    across all 12 qualifier combinations, generated via a local
    PYBIND11_REBIND_MEMBER_PTR macro.
  • Add detail::adapt_member_ptr<Derived>(pmf) — shared constexpr helper
    called by all method_adaptor overloads: runs the is_accessible_base_of
    static_assert then returns the implicitly-cast pointer.
  • Replace the ad-hoc method_adaptor overloads with a PYBIND11_METHOD_ADAPTOR
    macro covering all 11 qualified variants (plus the no-qualifier overload
    written explicitly to avoid MSVC C4003). All overloads are constexpr.
  • Add unconditional & / const & method_adaptor overloads (no #ifdef
    guard) to fix C++14 builds where noexcept is stripped from the type but
    the & qualifier is not.

include/pybind11/numpy.h

  • Add vectorize overloads for noexcept free function pointers.

Tests

  • test_methods_and_attributes: NoexceptDerived / NoexceptUnregisteredBase
    (C++17 noexcept from unregistered base), RValueRefDerived /
    RValueRefUnregisteredBase (&&/const && methods that move-out state to
    confirm std::move(*c).*f semantics), NoexceptOverloaded (overload_cast
    with noexcept), static_asserts verifying method_adaptor preserves all
    qualifier combinations.
  • test_buffers: def_buffer with noexcept member function pointers.
  • test_numpy_vectorize: vectorize with noexcept free function pointers.

Compatibility

  • No behaviour change for existing code — all new cpp_function constructors
    and method_adaptor overloads are additive and selected only for the new
    type signatures.
  • C++14 compatible (noexcept constructors/overloads gated on
    __cpp_noexcept_function_type).
  • MSVC C4003 avoided by writing the no-qualifier specializations explicitly
    rather than invoking macros with an empty argument.
  • clang-tidy bugprone-macro-parentheses suppressed with NOLINTBEGIN /
    NOLINTEND around the macro definitions (the qualifiers argument appears
    in type position where parenthesizing it would be invalid C++).

Suggested changelog entry:

  • Fix binding of noexcept and ref-qualified (&, &&) methods
    inherited from unregistered base classes in C++17, where noexcept is
    part of the function type. method_adaptor now has specializations for
    all qualifier combinations (const, &, const &, &&,
    const &&, and their noexcept variants), and cpp_function gains
    matching constructors. &&-qualified methods correctly use
    std::move(*self).*f in the generated lambda.

@Skylion007 Skylion007 requested a review from rwgk February 20, 2026 18:49
@Skylion007 Skylion007 force-pushed the skylion007/cpp17-claude-noexcept-2026-02-20 branch from a7a3ed9 to e8b0584 Compare February 20, 2026 19:36
@Skylion007 Skylion007 force-pushed the skylion007/cpp17-claude-noexcept-2026-02-20 branch from e8b0584 to b303194 Compare February 20, 2026 19:48
@Skylion007 Skylion007 force-pushed the skylion007/cpp17-claude-noexcept-2026-02-20 branch from ad30bc0 to f530adb Compare February 20, 2026 20:26
@Skylion007 Skylion007 changed the title Strip noexcept from cpp17 function type bindings Improve coverage of most method types Feb 21, 2026
@Skylion007 Skylion007 marked this pull request as ready for review February 21, 2026 20:30
@Skylion007 Skylion007 changed the title Improve coverage of most method types fix: bind noexcept and ref-qualified methods from unregistered base classes Feb 21, 2026
// (which match plain Return(Args...)) work correctly with noexcept callables (issue #2234).
template <typename Function, typename F = remove_reference_t<Function>>
using function_signature_t = conditional_t<
using function_signature_t = remove_noexcept_t<conditional_t<
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should I normalize it here for all pybind11? Or keep it as utility that and create a new alias that always strips the noexcept

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant