From 97844be578860cdf853b4c560cb7669670d7b12b Mon Sep 17 00:00:00 2001 From: Henry Schreiner Date: Wed, 22 Apr 2026 17:15:56 -0400 Subject: [PATCH] fix: avoid dangling-reference in sample tuple conversion for C++23 compatibility Under C++23 P2255R2 (Type Traits To Detect References Binding To Temporaries), std::tuple's converting constructor is deleted when a reference element would bind to a temporary. This broke code that converts tuple to tuple, since the int is first converted to a temporary double. Fix: strip cv-qualifiers and reference from the deduced sample argument types in accumulator_traits. This makes accumulator_traits>::args == tuple instead of tuple, so the compile-time convertibility check is_convertible, tuple> succeeds. Runtime behavior is unchanged; accumulators still receive arguments through their const double& parameter. Signed-off-by: Henry Schreiner Assisted-by: OpenCode:Kimi-K2.6 --- .../boost/histogram/detail/accumulator_traits.hpp | 15 ++++++++++----- test/accumulators_collector_test.cpp | 2 +- test/accumulators_mean_test.cpp | 2 +- test/detail_accumulator_traits_test.cpp | 2 +- 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/include/boost/histogram/detail/accumulator_traits.hpp b/include/boost/histogram/detail/accumulator_traits.hpp index e50503bf..76269ea8 100644 --- a/include/boost/histogram/detail/accumulator_traits.hpp +++ b/include/boost/histogram/detail/accumulator_traits.hpp @@ -31,24 +31,29 @@ struct accumulator_traits_holder { // member function pointer with weight_type as first argument is better match template -accumulator_traits_holder accumulator_traits_impl_call_op( +accumulator_traits_holder>...> +accumulator_traits_impl_call_op( R (T::*)(boost::histogram::weight_type, Ts...)); template -accumulator_traits_holder accumulator_traits_impl_call_op( +accumulator_traits_holder>...> +accumulator_traits_impl_call_op( R (T::*)(boost::histogram::weight_type&, Ts...)); template -accumulator_traits_holder accumulator_traits_impl_call_op( +accumulator_traits_holder>...> +accumulator_traits_impl_call_op( R (T::*)(boost::histogram::weight_type&&, Ts...)); template -accumulator_traits_holder accumulator_traits_impl_call_op( +accumulator_traits_holder>...> +accumulator_traits_impl_call_op( R (T::*)(const boost::histogram::weight_type&, Ts...)); // member function pointer only considered if all specializations above fail template -accumulator_traits_holder accumulator_traits_impl_call_op(R (T::*)(Ts...)); +accumulator_traits_holder>...> +accumulator_traits_impl_call_op(R (T::*)(Ts...)); template auto accumulator_traits_impl(T&, priority<2>) diff --git a/test/accumulators_collector_test.cpp b/test/accumulators_collector_test.cpp index 3207614a..2f9f841c 100644 --- a/test/accumulators_collector_test.cpp +++ b/test/accumulators_collector_test.cpp @@ -30,7 +30,7 @@ bool operator==(const span& a, const std::vector& b) { int main() { using traits = detail::accumulator_traits>; static_assert(!traits::weight_support, ""); - static_assert(std::is_same>::value, ""); + static_assert(std::is_same>::value, ""); { accumulators::collector> acc; diff --git a/test/accumulators_mean_test.cpp b/test/accumulators_mean_test.cpp index bf737803..f8a4e8ed 100644 --- a/test/accumulators_mean_test.cpp +++ b/test/accumulators_mean_test.cpp @@ -24,7 +24,7 @@ int main() { using traits = detail::accumulator_traits; static_assert(traits::weight_support, ""); - static_assert(std::is_same>::value, ""); + static_assert(std::is_same>::value, ""); // basic interface, string conversion { diff --git a/test/detail_accumulator_traits_test.cpp b/test/detail_accumulator_traits_test.cpp index 6a36ba6e..979b0ce0 100644 --- a/test/detail_accumulator_traits_test.cpp +++ b/test/detail_accumulator_traits_test.cpp @@ -72,7 +72,7 @@ int main() { }; BOOST_TEST(dtl::accumulator_traits::weight_support); - BOOST_TEST_TRAIT_SAME(typename dtl::accumulator_traits::args, std::tuple); + BOOST_TEST_TRAIT_SAME(typename dtl::accumulator_traits::args, std::tuple); struct B1 { int operator+=(int);