Merge branch 'master' into chiphogg/switch-to-mag

This commit is contained in:
Chip Hogg
2022-05-12 16:12:01 +00:00
165 changed files with 2468 additions and 1522 deletions
+28 -2
View File
@@ -20,7 +20,7 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
cmake_minimum_required(VERSION 3.15)
cmake_minimum_required(VERSION 3.19)
# core library options
set(${projectPrefix}DOWNCAST_MODE ON CACHE STRING "Select downcasting mode")
@@ -34,7 +34,33 @@ include(CheckLibcxxInUse)
check_libcxx_in_use(${projectPrefix}LIBCXX)
# core library definition
add_library(mp-units-core INTERFACE)
add_library(
mp-units-core
INTERFACE
include/units/base_dimension.h
include/units/chrono.h
include/units/concepts.h
include/units/customization_points.h
include/units/derived_dimension.h
include/units/exponent.h
include/units/generic/angle.h
include/units/generic/dimensionless.h
include/units/kind.h
include/units/magnitude.h
include/units/math.h
include/units/point_origin.h
include/units/prefix.h
include/units/quantity.h
include/units/quantity_cast.h
include/units/quantity_kind.h
include/units/quantity_point.h
include/units/quantity_point_kind.h
include/units/random.h
include/units/ratio.h
include/units/reference.h
include/units/symbol_text.h
include/units/unit.h
)
target_compile_features(mp-units-core INTERFACE cxx_std_20)
target_link_libraries(mp-units-core INTERFACE gsl::gsl-lite)
target_include_directories(
+1 -2
View File
@@ -50,8 +50,7 @@ namespace units {
* @tparam Symbol an unique identifier of the base dimension used to provide dimensional analysis support
* @tparam U a base unit to be used for this base dimension
*/
template<basic_fixed_string Symbol, Unit U>
requires U::is_named
template<basic_fixed_string Symbol, NamedUnit U>
struct base_dimension {
static constexpr auto symbol = Symbol; ///< Unique base dimension identifier
using base_unit = U; ///< Base unit adopted for this dimension
+14 -16
View File
@@ -37,25 +37,14 @@
namespace units {
// PrefixFamily
struct prefix_family;
/**
* @brief A concept matching a prefix family
*
* Satisfied by all types derived from `prefix_family`
*/
template<typename T>
concept PrefixFamily = std::derived_from<T, prefix_family>;
// Prefix
namespace detail {
template<PrefixFamily PF, ratio R>
template<ratio R>
struct prefix_base;
template<PrefixFamily PF, ratio R>
void to_prefix_base(const volatile prefix_base<PF, R>*);
template<ratio R>
void to_prefix_base(const volatile prefix_base<R>*);
} // namespace detail
@@ -87,9 +76,18 @@ void to_base_scaled_unit(const volatile scaled_unit<M, U>*);
template<typename T>
concept Unit = requires(T* t) { detail::to_base_scaled_unit(t); };
namespace detail {
template<typename>
inline constexpr bool is_named = false;
}
template<typename T>
concept NamedUnit = Unit<T> && detail::is_named<T>;
// BaseDimension
template<basic_fixed_string Symbol, Unit U>
requires U::is_named
template<basic_fixed_string Symbol, NamedUnit U>
struct base_dimension;
namespace detail {
@@ -34,7 +34,7 @@ inline constexpr bool same_scaled_units = false;
template<typename... Es, Unit... Us>
inline constexpr bool same_scaled_units<exponent_list<Es...>, Us...> = (UnitOf<Us, typename Es::dimension> && ...);
// derived_unit
// derived_scaled_unit
template<Unit... Us, typename... Es>
constexpr Magnitude auto derived_mag(exponent_list<Es...>)
@@ -44,6 +44,6 @@ constexpr Magnitude auto derived_mag(exponent_list<Es...>)
}
template<DerivedDimension D, Unit... Us>
using derived_unit = scaled_unit<derived_mag<Us...>(typename D::recipe()), typename D::coherent_unit::reference>;
using derived_scaled_unit = scaled_unit<derived_mag<Us...>(typename D::recipe()), typename D::coherent_unit::reference>;
} // namespace units::detail
+18 -31
View File
@@ -26,6 +26,7 @@
#include <units/bits/external/text_tools.h>
#include <units/derived_dimension.h>
#include <units/prefix.h>
#include <units/unit.h>
namespace units::detail {
@@ -57,28 +58,19 @@ constexpr auto ratio_text()
}
}
template<ratio R, typename PrefixFamily, std::size_t SymbolLen>
template<Unit U, ratio R, std::size_t SymbolLen>
constexpr auto prefix_or_ratio_text()
{
if constexpr (R.num == 1 && R.den == 1 && R.exp == 0) {
// no ratio/prefix
return basic_fixed_string("");
} else {
if constexpr (!is_same_v<PrefixFamily, no_prefix>) {
// try to form a prefix
using prefix = downcast<detail::prefix_base<PrefixFamily, R>>;
// try to form a prefix
using prefix = downcast<detail::prefix_base<R>>;
if constexpr (!is_same_v<prefix, prefix_base<PrefixFamily, R>>) {
// print as a prefixed unit
return prefix::symbol;
} else {
// print as a ratio of the coherent unit
constexpr auto txt = ratio_text<R>();
if constexpr (SymbolLen > 0 && txt.standard().size() > 0)
return txt + basic_fixed_string(" ");
else
return txt;
}
if constexpr (can_be_prefixed<U> && !is_same_v<prefix, prefix_base<R>>) {
// print as a prefixed unit
return prefix::symbol;
} else {
// print as a ratio of the coherent unit
constexpr auto txt = ratio_text<R>();
@@ -110,7 +102,7 @@ template<Exponent Exp>
constexpr auto exponent_list_with_named_units(Exp)
{
using dim = TYPENAME Exp::dimension;
if constexpr (dimension_unit<dim>::is_named) {
if constexpr (NamedUnit<dimension_unit<dim>>) {
return exponent_list<Exp>();
} else {
using recipe = TYPENAME dim::recipe;
@@ -147,21 +139,16 @@ constexpr auto unit_text()
// print as a prefix or ratio of a coherent unit
using coherent_unit = dimension_unit<Dim>;
if constexpr (has_symbol<coherent_unit>) {
// use predefined coherent unit symbol
constexpr auto symbol_text = coherent_unit::symbol;
constexpr auto prefix_txt =
prefix_or_ratio_text<as_ratio(U::mag / coherent_unit::mag), typename U::reference::prefix_family,
symbol_text.standard().size()>();
return prefix_txt + symbol_text;
} else {
// use derived dimension ingredients to create a unit symbol
constexpr auto symbol_text = derived_dimension_unit_text<Dim>();
constexpr auto prefix_txt =
prefix_or_ratio_text<as_ratio(U::mag / coherent_unit::mag), typename U::reference::prefix_family,
symbol_text.standard().size()>();
return prefix_txt + symbol_text;
}
constexpr auto symbol_text = []() {
if constexpr (has_symbol<coherent_unit>)
return coherent_unit::symbol;
else
return derived_dimension_unit_text<Dim>();
}();
constexpr auto prefix_txt =
prefix_or_ratio_text<U, as_ratio(U::mag / coherent_unit::mag), symbol_text.standard().size()>();
return prefix_txt + symbol_text;
}
}
+1 -1
View File
@@ -32,7 +32,7 @@
namespace units {
struct radian : named_unit<radian, "rad", isq::si::prefix> {};
struct radian : named_unit<radian, "rad"> {};
template<Unit U = radian>
struct dim_angle : base_dimension<"A", U> {};
@@ -30,8 +30,8 @@
namespace units {
struct one : named_unit<one, "", no_prefix> {};
struct percent : named_scaled_unit<percent, "%", no_prefix, as_magnitude<ratio(1, 100)>(), one> {};
struct one : derived_unit<one> {};
struct percent : named_scaled_unit<percent, "%", as_magnitude<ratio(1, 100)>(), one> {};
/**
* @brief Dimension one
+5 -24
View File
@@ -32,26 +32,10 @@
namespace units {
/**
* @brief The base for all prefix families
*
* Every prefix family should inherit from this type to satisfy PrefixFamily concept.
*/
struct prefix_family {};
/**
* @brief No prefix possible for the unit
*
* This is a special prefix type tag specifying that the unit can not be scaled with any kind
* of the prefix.
*/
struct no_prefix : prefix_family {};
namespace detail {
template<PrefixFamily PF, ratio R>
struct prefix_base : downcast_base<prefix_base<PF, R>> {
using prefix_family = PF;
template<ratio R>
struct prefix_base : downcast_base<prefix_base<R>> {
static constexpr Magnitude auto mag = as_magnitude<R>();
};
@@ -64,17 +48,14 @@ struct prefix_base : downcast_base<prefix_base<PF, R>> {
* - when defining a prefixed_unit its ratio is used to scale the reference unit and its
* symbol is used to prepend to the symbol of referenced unit
* - when printing the symbol of a scaled unit that was not predefined by the user but its
* factor matches ratio of a prefix from the specified prefix family, its symbol will be
* prepended to the symbol of the unit
* factor matches ratio of a prefix, its symbol will be prepended to the symbol of the unit
*
* @tparam Child inherited class type used by the downcasting facility (CRTP Idiom)
* @tparam PF a type of prefix family
* @tparam Symbol a text representation of the prefix
* @tparam R factor to be used to scale a unit
*/
template<typename Child, PrefixFamily PF, basic_symbol_text Symbol, ratio R>
requires(!std::same_as<PF, no_prefix>)
struct prefix : downcast_dispatch<Child, detail::prefix_base<PF, R>, downcast_mode::on> {
template<typename Child, basic_symbol_text Symbol, ratio R>
struct prefix : downcast_dispatch<Child, detail::prefix_base<R>, downcast_mode::on> {
static constexpr auto symbol = Symbol;
};
+22 -17
View File
@@ -45,6 +45,12 @@ inline constexpr auto make_quantity = [](auto&& v) {
return quantity<typename decltype(R)::dimension, typename decltype(R)::unit, Rep>(std::forward<decltype(v)>(v));
};
template<typename T>
concept quantity_one =
Quantity<T> &&
(std::same_as<typename T::dimension, dim_one> || std::same_as<typename T::dimension, unknown_dimension<>>) &&
detail::equivalent_unit<typename T::unit, typename T::dimension, ::units::one, typename T::dimension>::value;
} // namespace detail
template<typename T>
@@ -133,8 +139,7 @@ public:
template<typename Value>
requires(safe_convertible_to_<std::remove_cvref_t<Value>, rep>)
constexpr explicit(!(std::same_as<dimension, dim_one> && std::same_as<unit, ::units::one>)) quantity(Value&& v) :
number_(std::forward<Value>(v))
constexpr explicit(!detail::quantity_one<quantity>) quantity(Value&& v) : number_(std::forward<Value>(v))
{
}
@@ -252,9 +257,9 @@ public:
number_ *= rhs;
return *this;
}
template<typename Rep2>
constexpr quantity& operator*=(const dimensionless<units::one, Rep2>& rhs)
requires requires(rep a, const Rep2 b) {
template<detail::quantity_one Q>
constexpr quantity& operator*=(const Q& rhs)
requires requires(rep a, const typename Q::rep b) {
{
a *= b
} -> std::same_as<rep&>;
@@ -276,15 +281,15 @@ public:
number_ /= rhs;
return *this;
}
template<typename Rep2>
constexpr quantity& operator/=(const dimensionless<units::one, Rep2>& rhs)
requires requires(rep a, const Rep2 b) {
template<detail::quantity_one Q>
constexpr quantity& operator/=(const Q& rhs)
requires requires(rep a, const typename Q::rep b) {
{
a /= b
} -> std::same_as<rep&>;
}
{
gsl_ExpectsAudit(rhs.number() != quantity_values<Rep2>::zero());
gsl_ExpectsAudit(rhs.number() != quantity_values<typename Q::rep>::zero());
number_ /= rhs.number();
return *this;
}
@@ -302,15 +307,15 @@ public:
return *this;
}
template<typename Rep2>
constexpr quantity& operator%=(const dimensionless<units::one, Rep2>& rhs)
requires(!floating_point_<rep>) && (!floating_point_<Rep2>) && requires(rep a, const Rep2 b) {
{
a %= b
} -> std::same_as<rep&>;
}
template<detail::quantity_one Q>
constexpr quantity& operator%=(const Q& rhs)
requires(!floating_point_<rep>) && (!floating_point_<typename Q::rep>) && requires(rep a, const typename Q::rep b) {
{
a %= b
} -> std::same_as<rep&>;
}
{
gsl_ExpectsAudit(rhs.number() != quantity_values<Rep2>::zero());
gsl_ExpectsAudit(rhs.number() != quantity_values<typename Q::rep>::zero());
number_ %= rhs.number();
return *this;
}
+9 -6
View File
@@ -54,6 +54,10 @@ inline constexpr auto downcasted_kind_fn = [](auto q) {
template<QuantityKind QK>
inline constexpr auto& downcasted_kind = downcasted_kind_fn<typename QK::kind_type::base_kind>;
template<typename T, typename K>
concept quantity_kind_one = QuantityKind<T> && equivalent<typename T::kind_type, downcast_kind<K, dim_one>> &&
detail::quantity_one<typename T::quantity_type>;
} // namespace detail
/**
@@ -198,8 +202,8 @@ public:
q_ *= rhs;
return *this;
}
template<typename Rep2>
constexpr quantity_kind& operator*=(const quantity_kind<downcast_kind<K, dim_one>, units::one, Rep2>& rhs)
template<detail::quantity_kind_one<K> QK>
constexpr quantity_kind& operator*=(const QK& rhs)
requires requires(quantity_type q) { q *= rhs.common(); }
{
q_ *= rhs.common();
@@ -214,8 +218,8 @@ public:
q_ /= rhs;
return *this;
}
template<typename Rep2>
constexpr quantity_kind& operator/=(const quantity_kind<downcast_kind<K, dim_one>, units::one, Rep2>& rhs)
template<detail::quantity_kind_one<K> QK>
constexpr quantity_kind& operator/=(const QK& rhs)
requires requires(quantity_type q) { q /= rhs.common(); }
{
q_ /= rhs.common();
@@ -233,8 +237,7 @@ public:
template<QuantityKind QK>
constexpr quantity_kind& operator%=(const QK& rhs)
requires(QuantityKindEquivalentTo<QK, quantity_kind> ||
std::same_as<typename QK::kind_type, downcast_kind<K, dim_one>>) &&
requires(QuantityKindEquivalentTo<QK, quantity_kind> || detail::quantity_kind_one<QK, K>) &&
requires(quantity_type q) { q %= rhs.common(); }
{
gsl_ExpectsAudit(rhs.common().number() != quantity_values<typename QK::rep>::zero());
+6 -6
View File
@@ -78,16 +78,16 @@ struct basic_symbol_text {
{
detail::validate_ascii_string<N>(std.data_);
}
constexpr basic_symbol_text(const StandardCharT (&std)[N + 1], const char (&a)[M + 1]) noexcept :
standard_(std), ascii_(a)
constexpr basic_symbol_text(const StandardCharT (&std)[N + 1], const char (&ascii)[M + 1]) noexcept :
standard_(std), ascii_(ascii)
{
detail::validate_ascii_string<M>(a);
detail::validate_ascii_string<M>(ascii);
}
constexpr basic_symbol_text(const basic_fixed_string<StandardCharT, N>& std,
const basic_fixed_string<char, M>& a) noexcept :
standard_(std), ascii_(a)
const basic_fixed_string<char, M>& ascii) noexcept :
standard_(std), ascii_(ascii)
{
detail::validate_ascii_string<M>(a.data_);
detail::validate_ascii_string<M>(ascii.data_);
}
[[nodiscard]] constexpr auto& standard() { return standard_; }
+74 -78
View File
@@ -26,7 +26,7 @@
#include <units/bits/external/downcasting.h>
// IWYU pragma: begin_exports
#include <units/bits/derived_unit.h>
#include <units/bits/derived_scaled_unit.h>
#include <units/bits/external/fixed_string.h>
#include <units/magnitude.h>
#include <units/prefix.h>
@@ -36,6 +36,13 @@
namespace units {
namespace detail {
template<typename>
inline constexpr bool can_be_prefixed = false;
} // namespace detail
/**
* @brief A common point for a hierarchy of units
*
@@ -63,58 +70,34 @@ using downcast_unit = downcast<scaled_unit<M, typename dimension_unit<D>::refere
template<Unit U1, Unit U2>
struct same_unit_reference : is_same<typename U1::reference, typename U2::reference> {};
/**
* @brief An unnamed unit
*
* Defines a new unnamed (in most cases coherent) derived unit of a specific derived dimension
* and it should be passed in this dimension's definition.
*
* @tparam Child inherited class type used by the downcasting facility (CRTP Idiom)
*/
template<typename Child>
struct unit : downcast_dispatch<Child, scaled_unit<as_magnitude<1>(), Child>> {
static constexpr bool is_named = false;
using prefix_family = no_prefix;
};
/**
* @brief A named unit
*
* Defines a named (in most cases coherent) unit that is then passed to a dimension definition.
* A named unit may be used by other units defined with the prefix of the same type, unless
* no_prefix is provided for PF template parameter (in such a case it is impossible to define
* a prefix unit based on this one).
* A named unit may be composed with a prefix to create a prefixed_unit.
*
* @tparam Child inherited class type used by the downcasting facility (CRTP Idiom)
* @tparam Symbol a short text representation of the unit
* @tparam PF no_prefix or a type of prefix family
*/
template<typename Child, basic_symbol_text Symbol, PrefixFamily PF>
template<typename Child, basic_symbol_text Symbol>
struct named_unit : downcast_dispatch<Child, scaled_unit<as_magnitude<1>(), Child>> {
static constexpr bool is_named = true;
static constexpr auto symbol = Symbol;
using prefix_family = PF;
};
/**
* @brief A scaled unit
* @brief A named scaled unit
*
* Defines a new named unit that is a scaled version of another unit. Such unit can be used by
* other units defined with the prefix of the same type, unless no_prefix is provided for PF
* template parameter (in such a case it is impossible to define a prefix unit based on this
* one).
* Defines a new named unit that is a scaled version of another unit.
* A named unit may be composed with a prefix to create a prefixed_unit.
*
* @tparam Child inherited class type used by the downcasting facility (CRTP Idiom)
* @tparam Symbol a short text representation of the unit
* @tparam PF no_prefix or a type of prefix family
* @tparam M the Magnitude by which to scale U
* @tparam U a reference unit to scale
*/
template<typename Child, basic_symbol_text Symbol, PrefixFamily PF, Magnitude auto M, Unit U>
template<typename Child, basic_symbol_text Symbol, Magnitude auto M, Unit U>
struct named_scaled_unit : downcast_dispatch<Child, scaled_unit<M * U::mag, typename U::reference>> {
static constexpr bool is_named = true;
static constexpr auto symbol = Symbol;
using prefix_family = PF;
};
/**
@@ -128,14 +111,23 @@ struct named_scaled_unit : downcast_dispatch<Child, scaled_unit<M * U::mag, type
* @tparam P prefix to be appied to the reference unit
* @tparam U reference unit
*/
template<typename Child, Prefix P, Unit U>
requires U::is_named && std::same_as<typename P::prefix_family, typename U::prefix_family>
template<typename Child, Prefix P, NamedUnit U>
requires detail::can_be_prefixed<U>
struct prefixed_unit : downcast_dispatch<Child, scaled_unit<P::mag * U::mag, typename U::reference>> {
static constexpr bool is_named = true;
static constexpr auto symbol = P::symbol + U::symbol;
using prefix_family = no_prefix;
};
/**
* @brief A coherent unit of a derived quantity
*
* Defines a new coherent unit of a derived quantity. It should be passed as a coherent unit
* in the dimension's definition for such a quantity.
*
* @tparam Child inherited class type used by the downcasting facility (CRTP Idiom)
*/
template<typename Child>
struct derived_unit : downcast_dispatch<Child, scaled_unit<ratio(1), Child>> {};
/**
* @brief A unit with a deduced ratio and symbol
*
@@ -149,36 +141,10 @@ struct prefixed_unit : downcast_dispatch<Child, scaled_unit<P::mag * U::mag, typ
* @tparam U the unit of the first composite dimension from provided derived dimension's recipe
* @tparam URest the units for the rest of dimensions from the recipe
*/
template<typename Child, DerivedDimension Dim, Unit U, Unit... URest>
requires detail::same_scaled_units<typename Dim::recipe, U, URest...> &&
(U::is_named && (URest::is_named && ... && true))
struct derived_unit : downcast_dispatch<Child, detail::derived_unit<Dim, U, URest...>> {
static constexpr bool is_named = false;
static constexpr auto symbol = detail::derived_symbol_text<Dim, U, URest...>();
using prefix_family = no_prefix;
};
/**
* @brief A named unit with a deduced ratio
*
* Defines a new unit with a deduced ratio and the given symbol based on the recipe from the provided
* derived dimension. The number and order of provided units should match the recipe of the
* derived dimension. All of the units provided should also be a named ones so it is possible
* to create a deduced symbol text.
*
* @tparam Child inherited class type used by the downcasting facility (CRTP Idiom)
* @tparam Dim a derived dimension recipe to use for deduction
* @tparam Symbol a short text representation of the unit
* @tparam PF no_prefix or a type of prefix family
* @tparam U the unit of the first composite dimension from provided derived dimension's recipe
* @tparam URest the units for the rest of dimensions from the recipe
*/
template<typename Child, DerivedDimension Dim, basic_symbol_text Symbol, PrefixFamily PF, Unit U, Unit... URest>
template<typename Child, DerivedDimension Dim, NamedUnit U, NamedUnit... URest>
requires detail::same_scaled_units<typename Dim::recipe, U, URest...>
struct named_derived_unit : downcast_dispatch<Child, detail::derived_unit<Dim, U, URest...>> {
static constexpr bool is_named = true;
static constexpr auto symbol = Symbol;
using prefix_family = PF;
struct derived_scaled_unit : downcast_dispatch<Child, detail::derived_scaled_unit<Dim, U, URest...>> {
static constexpr auto symbol = detail::derived_symbol_text<Dim, U, URest...>();
};
/**
@@ -186,19 +152,14 @@ struct named_derived_unit : downcast_dispatch<Child, detail::derived_unit<Dim, U
*
* Defines a named alias for another unit. It is useful to assign alternative names and symbols
* to the already predefined units (i.e. "tonne" for "megagram").
* An alias unit may be used by other units defined with the prefix of the same type, unless
* no_prefix is provided for PF template parameter (in such a case it is impossible to define
* a prefix unit based on this one).
* A alias unit may be composed with a prefix to create a prefixed_alias_unit.
*
* @tparam U Unit for which an alias is defined
* @tparam Symbol a short text representation of the unit
* @tparam PF no_prefix or a type of prefix family
*/
template<Unit U, basic_symbol_text Symbol, PrefixFamily PF>
template<Unit U, basic_symbol_text Symbol>
struct alias_unit : U {
static constexpr bool is_named = true;
static constexpr auto symbol = Symbol;
using prefix_family = PF;
};
/**
@@ -215,20 +176,55 @@ struct alias_unit : U {
// TODO gcc bug: 95015
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=95015
// template<Unit U, Prefix P, AliasUnit AU>
// requires (!AliasUnit<U>) && std::same_as<typename P::prefix_family, typename AU::prefix_family>
template<Unit U, Prefix P, Unit AU>
requires AU::is_named && std::same_as<typename P::prefix_family, typename AU::prefix_family>
// requires (!AliasUnit<U>)
template<Unit U, Prefix P, NamedUnit AU>
requires detail::can_be_prefixed<AU>
struct prefixed_alias_unit : U {
static constexpr bool is_named = true;
static constexpr auto symbol = P::symbol + AU::symbol;
using prefix_family = no_prefix;
};
/**
* @brief Unknown unit
* @brief Unknown coherent unit
*
* Used as a coherent unit of an unknown dimension.
*/
struct unknown_coherent_unit : unit<unknown_coherent_unit> {};
struct unknown_coherent_unit : derived_unit<unknown_coherent_unit> {};
namespace detail {
template<typename Child, basic_symbol_text Symbol>
void is_named_impl(const volatile named_unit<Child, Symbol>*);
template<typename Child, basic_symbol_text Symbol, ratio R, typename U>
void is_named_impl(const volatile named_scaled_unit<Child, Symbol, R, U>*);
template<typename Child, typename P, typename U>
void is_named_impl(const volatile prefixed_unit<Child, P, U>*);
template<typename U, basic_symbol_text Symbol>
void is_named_impl(const volatile alias_unit<U, Symbol>*);
template<typename U, typename P, typename AU>
void is_named_impl(const volatile prefixed_alias_unit<U, P, AU>*);
template<Unit U>
inline constexpr bool is_named<U> = requires(U * u) { is_named_impl(u); };
template<typename Child, basic_symbol_text Symbol>
void can_be_prefixed_impl(const volatile named_unit<Child, Symbol>*);
template<typename Child, basic_symbol_text Symbol, ratio R, typename U>
void can_be_prefixed_impl(const volatile named_scaled_unit<Child, Symbol, R, U>*);
template<typename U, basic_symbol_text Symbol>
void can_be_prefixed_impl(const volatile alias_unit<U, Symbol>*);
template<Unit U>
inline constexpr bool can_be_prefixed<U> = requires(U * u) { can_be_prefixed_impl(u); };
template<ratio R, typename U>
inline constexpr bool can_be_prefixed<scaled_unit<R, U>> = can_be_prefixed<typename U::reference>;
} // namespace detail
} // namespace units