Skip to content

Commit d6fc602

Browse files
committed
fix: clang bug with Hidden Friends and default arguments fixed
1 parent e52305d commit d6fc602

File tree

3 files changed

+61
-7
lines changed

3 files changed

+61
-7
lines changed

src/core/include/mp-units/framework/dimension.h

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,13 +83,29 @@ struct dimension_interface {
8383
return is_same_v<Lhs, Rhs>;
8484
}
8585

86-
template<std::intmax_t Num, std::intmax_t Den = 1, Dimension D>
86+
// Clang <= 18 does not support default template arguments in friend function templates.
87+
// The two overloads below are a workaround. The intended library API (and the one that
88+
// should be proposed for ISO standardization) is a single function template:
89+
//
90+
// template<std::intmax_t Num, std::intmax_t Den = 1, Dimension D>
91+
// requires(Den != 0)
92+
// [[nodiscard]] friend consteval Dimension auto pow(D d)
93+
// {
94+
// return detail::expr_pow<Num, Den, derived_dimension, struct dimension_one>(d);
95+
// }
96+
template<std::intmax_t Num, std::intmax_t Den, Dimension D>
8797
requires(Den != 0)
8898
[[nodiscard]] friend consteval Dimension auto pow(D d)
8999
{
90100
return detail::expr_pow<Num, Den, derived_dimension, struct dimension_one>(d);
91101
}
92102

103+
template<std::intmax_t Num, Dimension D>
104+
[[nodiscard]] friend consteval Dimension auto pow(D d)
105+
{
106+
return detail::expr_pow<Num, 1, derived_dimension, struct dimension_one>(d);
107+
}
108+
93109
[[nodiscard]] friend consteval Dimension auto sqrt(Dimension auto d) { return pow<1, 2>(d); }
94110
[[nodiscard]] friend consteval Dimension auto cbrt(Dimension auto d) { return pow<1, 3>(d); }
95111
};

src/core/include/mp-units/framework/quantity_spec.h

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,19 @@ struct quantity_spec_interface_base {
207207
return is_same_v<Lhs, Rhs>;
208208
}
209209

210-
template<std::intmax_t Num, std::intmax_t Den = 1, QuantitySpec Q>
210+
// Clang <= 18 does not support default template arguments in friend function templates.
211+
// The two overloads below are a workaround. The intended library API (and the one that
212+
// should be proposed for ISO standardization) is a single function template:
213+
//
214+
// template<std::intmax_t Num, std::intmax_t Den = 1, QuantitySpec Q>
215+
// requires(Den != 0)
216+
// [[nodiscard]] friend consteval QuantitySpec auto pow(Q q)
217+
// {
218+
// return detail::clone_kind_of<Q{}>(
219+
// detail::expr_pow<Num, Den, derived_quantity_spec, struct dimensionless,
220+
// detail::type_list_of_quantity_spec_less>(detail::remove_kind(q)));
221+
// }
222+
template<std::intmax_t Num, std::intmax_t Den, QuantitySpec Q>
211223
requires(Den != 0)
212224
[[nodiscard]] friend consteval QuantitySpec auto pow(Q q)
213225
{
@@ -216,6 +228,14 @@ struct quantity_spec_interface_base {
216228
detail::remove_kind(q)));
217229
}
218230

231+
template<std::intmax_t Num, QuantitySpec Q>
232+
[[nodiscard]] friend consteval QuantitySpec auto pow(Q q)
233+
{
234+
return detail::clone_kind_of<Q{}>(
235+
detail::expr_pow<Num, 1, derived_quantity_spec, struct dimensionless, detail::type_list_of_quantity_spec_less>(
236+
detail::remove_kind(q)));
237+
}
238+
219239
[[nodiscard]] friend consteval QuantitySpec auto sqrt(QuantitySpec auto q) { return pow<1, 2>(q); }
220240
[[nodiscard]] friend consteval QuantitySpec auto cbrt(QuantitySpec auto q) { return pow<1, 3>(q); }
221241
};

src/core/include/mp-units/framework/unit.h

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -236,13 +236,29 @@ struct unit_interface {
236236
return kind_of<detail::get_associated_quantity(U{})>;
237237
}
238238

239-
template<std::intmax_t Num, std::intmax_t Den = 1, Unit U>
239+
// Clang <= 18 does not support default template arguments in friend function templates.
240+
// The two overloads below are a workaround. The intended library API (and the one that
241+
// should be proposed for ISO standardization) is a single function template:
242+
//
243+
// template<std::intmax_t Num, std::intmax_t Den = 1, Unit U>
244+
// requires(Den != 0)
245+
// [[nodiscard]] friend consteval Unit auto pow(U u)
246+
// {
247+
// return detail::expr_pow<Num, Den, derived_unit, struct one>(u);
248+
// }
249+
template<std::intmax_t Num, std::intmax_t Den, Unit U>
240250
requires(Den != 0)
241251
[[nodiscard]] friend consteval Unit auto pow(U u)
242252
{
243253
return detail::expr_pow<Num, Den, derived_unit, struct one>(u);
244254
}
245255

256+
template<std::intmax_t Num, Unit U>
257+
[[nodiscard]] friend consteval Unit auto pow(U u)
258+
{
259+
return detail::expr_pow<Num, 1, derived_unit, struct one>(u);
260+
}
261+
246262
[[nodiscard]] friend consteval Unit auto sqrt(Unit auto u) { return pow<1, 2>(u); }
247263
[[nodiscard]] friend consteval Unit auto cbrt(Unit auto u) { return pow<1, 3>(u); }
248264
[[nodiscard]] friend consteval Unit auto square(Unit auto u) { return pow<2>(u); }
@@ -359,14 +375,14 @@ struct named_unit<Symbol, QS, PO> : detail::unit_interface {
359375
* @tparam Unit a unit for which we provide a special name
360376
*/
361377
template<symbol_text Symbol, Unit auto U>
362-
requires(!Symbol.empty())
378+
requires(!Symbol.empty()) && detail::magnitude_is_positive<mp_units::get_canonical_unit(U).mag>
363379
struct named_unit<Symbol, U> : decltype(U)::_base_type_ {
364380
using _base_type_ = named_unit; // exposition only
365381
static constexpr auto _symbol_ = Symbol; ///< Unique unit identifier
366382
};
367383

368384
template<symbol_text Symbol, Unit auto U, PointOrigin auto PO>
369-
requires(!Symbol.empty())
385+
requires(!Symbol.empty()) && detail::magnitude_is_positive<mp_units::get_canonical_unit(U).mag>
370386
struct named_unit<Symbol, U, PO> : decltype(U)::_base_type_ {
371387
using _base_type_ = named_unit; // exposition only
372388
static constexpr auto _symbol_ = Symbol; ///< Unique unit identifier
@@ -383,15 +399,17 @@ struct named_unit<Symbol, U, PO> : decltype(U)::_base_type_ {
383399
* @tparam QuantitySpec a specification of a quantity to be measured with this unit
384400
*/
385401
template<symbol_text Symbol, Unit auto U, detail::QuantityKindSpec auto QS>
386-
requires(!Symbol.empty()) && (QS.dimension == detail::get_associated_quantity(U).dimension)
402+
requires(!Symbol.empty()) && (QS.dimension == detail::get_associated_quantity(U).dimension) &&
403+
detail::magnitude_is_positive<mp_units::get_canonical_unit(U).mag>
387404
struct named_unit<Symbol, U, QS> : decltype(U)::_base_type_ {
388405
using _base_type_ = named_unit; // exposition only
389406
static constexpr auto _symbol_ = Symbol; ///< Unique unit identifier
390407
static constexpr auto _quantity_spec_ = QS;
391408
};
392409

393410
template<symbol_text Symbol, Unit auto U, detail::QuantityKindSpec auto QS, PointOrigin auto PO>
394-
requires(!Symbol.empty()) && (QS.dimension == detail::get_associated_quantity(U).dimension)
411+
requires(!Symbol.empty()) && (QS.dimension == detail::get_associated_quantity(U).dimension) &&
412+
detail::magnitude_is_positive<mp_units::get_canonical_unit(U).mag>
395413
struct named_unit<Symbol, U, QS, PO> : decltype(U)::_base_type_ {
396414
using _base_type_ = named_unit; // exposition only
397415
static constexpr auto _symbol_ = Symbol; ///< Unique unit identifier

0 commit comments

Comments
 (0)