@@ -1467,8 +1467,27 @@ inline constexpr auto as() -> auto
14671467 }
14681468}
14691469
1470- template < typename C, typename X >
1471- auto as (X const & x CPP2_SOURCE_LOCATION_PARAM_WITH_DEFAULT) -> decltype(auto ) {
1470+ template < typename C, auto x >
1471+ requires (std::is_same_v<C, std::string> && std::is_integral_v<CPP2_TYPEOF(x)>)
1472+ inline constexpr auto as () -> auto
1473+ {
1474+ return cpp2::to_string (CPP2_FORWARD (x));
1475+ }
1476+
1477+ template < typename C >
1478+ auto as (auto && x CPP2_SOURCE_LOCATION_PARAM_WITH_DEFAULT) -> decltype(auto )
1479+ // This "requires" list may need to be tweaked further. The idea is to have
1480+ // this function used for all the cases it's supposed to cover, but not
1481+ // hide user-supplied extensions (such as the ones later in this file for
1482+ // std:: polymorphic types like any/optional/variant)
1483+ requires (
1484+ (std::is_scalar_v<CPP2_TYPEOF(x)> && !std::is_enum_v<CPP2_TYPEOF(x)>)
1485+ || std::is_floating_point_v<CPP2_TYPEOF(x)>
1486+ || std::is_base_of_v<C, CPP2_TYPEOF(x)>
1487+ || std::is_base_of_v<CPP2_TYPEOF(x), C>
1488+ || requires { C{CPP2_FORWARD (x)}; }
1489+ )
1490+ {
14721491 if constexpr (
14731492 std::is_floating_point_v<C> &&
14741493 std::is_floating_point_v<CPP2_TYPEOF (x)> &&
@@ -1487,65 +1506,57 @@ auto as(X const& x CPP2_SOURCE_LOCATION_PARAM_WITH_DEFAULT) -> decltype(auto) {
14871506 sizeof (CPP2_TYPEOF (x)) <= sizeof (C)
14881507 )
14891508 {
1490- const C c = static_cast <C>(x );
1509+ const C c = static_cast <C>(CPP2_FORWARD (x) );
14911510 type_safety.enforce ( // precondition check: must be round-trippable => not lossy
14921511 static_cast <CPP2_TYPEOF (x)>(c) == x && (c < C{}) == (x < CPP2_TYPEOF (x){}),
14931512 " dynamic lossy narrowing conversion attempt detected" CPP2_SOURCE_LOCATION_ARG
14941513 );
14951514 return CPP2_COPY (c);
14961515 }
1497- else if constexpr (std::is_same_v<C, std::string> && std::is_integral_v<X >) {
1498- return cpp2::to_string (x );
1516+ else if constexpr (std::is_same_v<C, std::string> && std::is_integral_v<CPP2_TYPEOF (x) >) {
1517+ return cpp2::to_string (CPP2_FORWARD (x) );
14991518 }
1500- else if constexpr (std::is_same_v<C, X >) {
1501- return x ;
1519+ else if constexpr (std::is_same_v<C, CPP2_TYPEOF (x) >) {
1520+ return CPP2_FORWARD (x) ;
15021521 }
1503- else if constexpr (std::is_base_of_v<C, X>) {
1504- return static_cast <C const &>(x);
1522+ else if constexpr (std::is_base_of_v<C, CPP2_TYPEOF (x)>) {
1523+ if constexpr (std::is_const_v<std::remove_reference_t <decltype (x)>>) {
1524+ return static_cast <C const &>(CPP2_FORWARD (x));
1525+ } else {
1526+ return static_cast <C&>(CPP2_FORWARD (x));
1527+ }
15051528 }
1506- else if constexpr (std::is_base_of_v<X, C>) {
1507- return Dynamic_cast<C const &>(x);
1529+ else if constexpr (std::is_base_of_v<CPP2_TYPEOF (x), C>) {
1530+ if constexpr (std::is_const_v<std::remove_reference_t <decltype (x)>>) {
1531+ return Dynamic_cast<C const &>(CPP2_FORWARD (x));
1532+ } else {
1533+ return Dynamic_cast<C&>(CPP2_FORWARD (x));
1534+ }
15081535 }
15091536 else if constexpr (
15101537 std::is_pointer_v<C>
1511- && std::is_pointer_v<X >
1512- && requires { requires std::is_base_of_v<deref_t <X >, deref_t <C>>; }
1538+ && std::is_pointer_v<CPP2_TYPEOF (x) >
1539+ && requires { requires std::is_base_of_v<deref_t <CPP2_TYPEOF (x) >, deref_t <C>>; }
15131540 )
15141541 {
1515- return Dynamic_cast<C>(x );
1542+ return Dynamic_cast<C>(CPP2_FORWARD (x) );
15161543 }
1517- else if constexpr (requires { C{x }; }) {
1544+ else if constexpr (requires { C{CPP2_FORWARD (x) }; }) {
15181545 // Experiment: Recognize the nested `::value_type` pattern for some dynamic library types
15191546 // like std::optional, and try to prevent accidental narrowing conversions even when
15201547 // those types themselves don't defend against them
1521- if constexpr ( requires { requires std::is_convertible_v<X , typename C::value_type>; } ) {
1522- if constexpr ( is_narrowing_v<typename C::value_type, X >) {
1548+ if constexpr ( requires { requires std::is_convertible_v<CPP2_TYPEOF (x) , typename C::value_type>; } ) {
1549+ if constexpr ( is_narrowing_v<typename C::value_type, CPP2_TYPEOF (x) >) {
15231550 return nonesuch;
15241551 }
15251552 }
1526- return C{x };
1553+ return C{CPP2_FORWARD (x) };
15271554 }
15281555 else {
15291556 return nonesuch;
15301557 }
15311558}
15321559
1533- template < typename C, typename X >
1534- auto as ( X& x ) -> decltype(auto ) {
1535- if constexpr (std::is_same_v<C, X>) {
1536- return x;
1537- }
1538- else if constexpr (std::is_base_of_v<C, X>) {
1539- return static_cast <C&>(x);
1540- }
1541- else if constexpr (std::is_base_of_v<X, C>) {
1542- return Dynamic_cast<C&>(x);
1543- }
1544- else {
1545- return as<C>(std::as_const (x));
1546- }
1547- }
1548-
15491560
15501561// -------------------------------------------------------------------------------------------------------------
15511562// std::variant is and as
0 commit comments