@@ -1467,8 +1467,27 @@ inline constexpr auto as() -> auto
1467
1467
}
1468
1468
}
1469
1469
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
+ {
1472
1491
if constexpr (
1473
1492
std::is_floating_point_v<C> &&
1474
1493
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) {
1487
1506
sizeof (CPP2_TYPEOF (x)) <= sizeof (C)
1488
1507
)
1489
1508
{
1490
- const C c = static_cast <C>(x );
1509
+ const C c = static_cast <C>(CPP2_FORWARD (x) );
1491
1510
type_safety.enforce ( // precondition check: must be round-trippable => not lossy
1492
1511
static_cast <CPP2_TYPEOF (x)>(c) == x && (c < C{}) == (x < CPP2_TYPEOF (x){}),
1493
1512
" dynamic lossy narrowing conversion attempt detected" CPP2_SOURCE_LOCATION_ARG
1494
1513
);
1495
1514
return CPP2_COPY (c);
1496
1515
}
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) );
1499
1518
}
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) ;
1502
1521
}
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
+ }
1505
1528
}
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
+ }
1508
1535
}
1509
1536
else if constexpr (
1510
1537
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>>; }
1513
1540
)
1514
1541
{
1515
- return Dynamic_cast<C>(x );
1542
+ return Dynamic_cast<C>(CPP2_FORWARD (x) );
1516
1543
}
1517
- else if constexpr (requires { C{x }; }) {
1544
+ else if constexpr (requires { C{CPP2_FORWARD (x) }; }) {
1518
1545
// Experiment: Recognize the nested `::value_type` pattern for some dynamic library types
1519
1546
// like std::optional, and try to prevent accidental narrowing conversions even when
1520
1547
// 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) >) {
1523
1550
return nonesuch;
1524
1551
}
1525
1552
}
1526
- return C{x };
1553
+ return C{CPP2_FORWARD (x) };
1527
1554
}
1528
1555
else {
1529
1556
return nonesuch;
1530
1557
}
1531
1558
}
1532
1559
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
-
1549
1560
1550
1561
// -------------------------------------------------------------------------------------------------------------
1551
1562
// std::variant is and as
0 commit comments