Skip to content

Commit 42ada15

Browse files
committed
fix: regression in #3271
1 parent bf1e50b commit 42ada15

File tree

3 files changed

+17
-9
lines changed

3 files changed

+17
-9
lines changed

include/pybind11/pybind11.h

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1966,27 +1966,29 @@ struct iterator_state {
19661966
};
19671967

19681968
// Note: these helpers take the iterator by non-const reference because some
1969-
// iterators in the wild can't be dereferenced when const.
1970-
template <typename Iterator>
1969+
// iterators in the wild can't be dereferenced when const. C++ needs the extra parens in decltype
1970+
// to enforce an lvalue. The & after Iterator is required for MSVC < 16.9. ResultType cannot be
1971+
// reused for result_type due to bugs in ICC, NVCC, and PGI. See #3293.
1972+
template <typename Iterator, typename ResultType = decltype((*std::declval<Iterator &>()))>
19711973
struct iterator_access {
1972-
using result_type = decltype((*std::declval<Iterator>()));
1974+
using result_type = decltype((*std::declval<Iterator &>()));
19731975
// NOLINTNEXTLINE(readability-const-return-type) // PR #3263
19741976
result_type operator()(Iterator &it) const {
19751977
return *it;
19761978
}
19771979
};
19781980

1979-
template <typename Iterator>
1981+
template <typename Iterator, typename ResultType = decltype(((*std::declval<Iterator &>()).first)) >
19801982
struct iterator_key_access {
1981-
using result_type = decltype(((*std::declval<Iterator>()).first));
1983+
using result_type = decltype(((*std::declval<Iterator &>()).first));
19821984
result_type operator()(Iterator &it) const {
19831985
return (*it).first;
19841986
}
19851987
};
19861988

1987-
template <typename Iterator>
1989+
template <typename Iterator, typename ResultType = decltype(((*std::declval<Iterator &>()).second))>
19881990
struct iterator_value_access {
1989-
using result_type = decltype(((*std::declval<Iterator>()).second));
1991+
using result_type = decltype(((*std::declval<Iterator &>()).second));
19901992
result_type operator()(Iterator &it) const {
19911993
return (*it).second;
19921994
}

tests/test_sequences_and_iterators.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -339,18 +339,23 @@ TEST_SUBMODULE(sequences_and_iterators, m) {
339339
.def("nonzero_values", [](const IntPairs& s) {
340340
return py::make_value_iterator(NonZeroIterator<std::pair<int, int>>(s.begin()), NonZeroSentinel());
341341
}, py::keep_alive<0, 1>())
342+
343+
// test single-argument make_iterator
342344
.def("simple_iterator", [](IntPairs& self) {
343345
return py::make_iterator(self);
344346
}, py::keep_alive<0, 1>())
345347
.def("simple_keys", [](IntPairs& self) {
346348
return py::make_key_iterator(self);
347349
}, py::keep_alive<0, 1>())
350+
.def("simple_values", [](IntPairs& self) {
351+
return py::make_value_iterator(self);
352+
}, py::keep_alive<0, 1>())
348353

349-
// test iterator with keep_alive (doesn't work so not used at runtime, but tests compile)
354+
// test iterator with keep_alive (doesn't work on this class so not used
355+
// at runtime, but tests need to be able to compile)
350356
.def("make_iterator_keep_alive", [](IntPairs& self) {
351357
return py::make_iterator(self, py::keep_alive<0, 1>());
352358
}, py::keep_alive<0, 1>())
353-
;
354359

355360
// test_iterater_referencing
356361
py::class_<NonCopyableInt>(m, "NonCopyableInt")

tests/test_sequences_and_iterators.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ def test_generalized_iterators_simple():
5959
(0, 5),
6060
]
6161
assert list(m.IntPairs([(1, 2), (3, 4), (0, 5)]).simple_keys()) == [1, 3, 0]
62+
assert list(m.IntPairs([(1, 2), (3, 4), (0, 5)]).simple_values()) == [2, 4, 5]
6263

6364

6465
def test_iterator_referencing():

0 commit comments

Comments
 (0)