pybind11: bind member functions via a forwarding lambda (fixes MSVC C2440 on overloaded auto-returning members of class templates)#38
Open
Fedr wants to merge 4 commits into
Conversation
…r-to-member cast Member functions were bound by disambiguating overloads with `static_cast<Ret (C::*)(Args) const>(&C::name)`. MSVC (through 19.5x / VS 2026) rejects that cast -- "C2440: ... none of the functions with this name in scope match the target type" -- when the member is overloaded, has a deduced (`auto`) return type, and the enclosing class is a template (e.g. MR::Buffer<T,I>::data(), which has auto + const/non-const overloads). GCC and Clang accept it. Bind via a captureless forwarding lambda instead: it selects the overload by the object's const-ness and never takes the address of an overloaded member. The lambda decays to a function pointer, so it stays a valid non-type template argument for TryAddFunc. This mirrors how friend functions are already bound (DETAIL_MB_PB11_FUNC_PTR_OR_LAMBDA_cl) and shares its extra-move-per-argument cost. MRE (Clang/GCC accept, MSVC fails): https://gcc.godbolt.org/z/nKnd5ffbE Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
https://developercommunity.visualstudio.com/t/C2440:-static_cast-to-select-an-overload/11107969 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…not the bare name The forwarding-lambda member binding called `self.name(args)`, where `name` is the bare declaration name. For template member functions whose template parameter is not deducible from the arguments (e.g. MR::VisualObject::tryGet<T>()), that fails to compile -- the old static_cast pinned the instantiation via the target signature, but a by-name call must name it explicitly. Call through `fullname_` instead, which carries the template arguments (`tryGet<MR::Foo>`); for non-template members it is just the bare name, so nothing else changes. `_pb11_C` is a concrete (non-dependent) type alias, so no `template` disambiguator is needed. Caught by building MeshLib's Python bindings (the previously-working Clang path regressed). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…plate limitation The existing note says the template-template-parameter specializations "don't work at all" on MSVC (MSVC's P0522 relaxed-template-template-argument matching is incomplete for partial specializations). Reported upstream: https://developercommunity.visualstudio.com/t/Partial-specialization-with-a-single-arg/11108062 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Member functions are bound by disambiguating overloads with a pointer-to-member cast:
MSVC (through 19.5x / VS 2026) rejects that cast when the member is overloaded, has a deduced (
auto) return type, and the enclosing class is a template — all three are required:GCC and Clang accept it. In MeshLib this hits e.g.
MR::Buffer<T,I>::data():Minimal repro (Clang 22 and GCC 15 compile; MSVC 19.5x fails), https://gcc.godbolt.org/z/nKnd5ffbE :
Every pointer-forming spelling fails the same way on MSVC (
static_cast, C-style cast, typed-init of a member pointer) — the failure is resolving the address of the overload set against a target type, not the cast itself.Fix
Bind the member via a captureless forwarding lambda instead of a pointer-to-member:
The lambda calls the function by name on a concrete object, so the const/non-const overload is selected by the object's const-ness and the address of an overloaded member is never taken. Being captureless, it decays (via the leading
+) to a function pointer, so it remains a valid non-type template argument toTryAddFunc, andTryAddFuncis unchanged.This mirrors how friend functions are already bound (
DETAIL_MB_PB11_FUNC_PTR_OR_LAMBDA_cl), reuses the existingDETAIL_MB_PB11_MAKE_PARAM_DECLS/_USEShelpers, and shares the same documented "extra move per argument" cost. Static methods get the no-selfvariant calling_pb11_C::name(...).Scope / notes
DETAIL_MB_PB11_DISPATCH_MEMBER_methodintargets/pybind11/core.h. The generated.cpp(fromdata_to_macros) is unchanged — the same macro invocations now expand to a lambda.DETAIL_MB_PB11_DISPATCH_MEMBER_conv_op) keep their cast: their return type is explicit (operator T), not deduced, so they don't hit the bug.name_unqualified to preserve virtual dispatch; this matches the common case where the method is defined in the class being bound. Worth a look for inherited members that are name-hidden in a derived class (the old cast named the base explicitly) — flagging for review.Validated end-to-end against MSVC by building MeshLib's Python bindings with MSVC (MeshLib PR #6272).