Troubleshooting Guide¶
This page is a guide to the most commonly encountered types of error, what they mean, and how to fix them.
The intended use case is to help you interpret an actual error in your code, at the point where you encounter it. To use this page, copy some relevant snippets from your compiler error, and then search the text of this page using your browser’s Find function.
Tip
To improve your chances of finding what you’re looking for, we include full compiler errors from gcc, clang, and MSVC, inline with the text. Naturally, this makes this page very long, so it’s not meant to be read straight through. Stick with your browser’s Find function.
Each section below lists one category of compiler error you might encounter when using the library. It explains what it means and how to solve it, and gives specific snippets of erroneous code, along with the compiler errors that each would yield.
Private constructor¶
Meaning: This means you passed a raw numeric value to an interface that expected a Quantity. It’s the “classic” error the units library aims to prevent.
Solution: Call the appropriate Quantity maker: instead of passing x
, pass meters(x)
,
(kilo(meters) / hour)(x)
, etc.
A note on quantity makers vs. constructors
Every other major units library lets you construct its Quantity types from raw numeric values;
it just makes that constructor explicit. Au goes further, and makes this constructor private.
The reason is to preserve unit safety at all callsites. We can’t know whether you made an alias
that doesn’t name the unit. For example, if you want everybody to measure lengths in Meters
in your codebase, you might provide a common alias like this:
If you did, then end users could write the following:
A core principle of the Au library is that the only way to enter or exit the library boundaries is to name the unit of measure, explicitly, at the callsite, like this:
This enables users who want to use this kind of “dimension-named alias” in their codebase to do so safely.
Example
Code
Compiler error (clang 14)
au/error_examples.cc:34:17: error: calling a private constructor of class 'au::Quantity<au::Seconds, double>'
set_timeout(0.5);
^
./au/quantity.hh:483:15: note: declared private here
constexpr Quantity(Rep value) : value_{value} {}
^
au/error_examples.cc:37:33: error: calling a private constructor of class 'au::Quantity<au::Meters, double>'
constexpr QuantityD<Meters> length{5.5};
^
./au/quantity.hh:483:15: note: declared private here
constexpr Quantity(Rep value) : value_{value} {}
^
Compiler error (gcc 10)
au/error_examples.cc: In function 'void au::example_private_constructor()':
au/error_examples.cc:34:20: error: 'constexpr au::Quantity<UnitT, RepT>::Quantity(au::Quantity<UnitT, RepT>::Rep) [with UnitT = au::Seconds; RepT = double; au::Quantity<UnitT, RepT>::Rep = double]' is private within this context
34 | set_timeout(0.5);
| ^
In file included from ./au/prefix.hh:18,
from ./au/chrono_interop.hh:20,
from ./au/au.hh:17,
from au/error_examples.cc:15:
./au/quantity.hh:483:15: note: declared private here
483 | constexpr Quantity(Rep value) : value_{value} {}
| ^~~~~~~~
au/error_examples.cc:37:43: error: 'constexpr au::Quantity<UnitT, RepT>::Quantity(au::Quantity<UnitT, RepT>::Rep) [with UnitT = au::Meters; RepT = double; au::Quantity<UnitT, RepT>::Rep = double]' is private within this context
37 | constexpr QuantityD<Meters> length{5.5};
| ^
In file included from ./au/prefix.hh:18,
from ./au/chrono_interop.hh:20,
from ./au/au.hh:17,
from au/error_examples.cc:15:
./au/quantity.hh:483:15: note: declared private here
483 | constexpr Quantity(Rep value) : value_{value} {}
| ^~~~~~~~
Compiler error (MSVC 2022 x64)
error_examples.cc(32): error C2248: 'au::Quantity<au::Seconds,double>::Quantity': cannot access private member declared in class 'au::Quantity<au::Seconds,double>'
D:\a\au\au\au.hh(6759): note: see declaration of 'au::Quantity<au::Seconds,double>::Quantity'
D:\a\au\au\au.hh(6403): note: see declaration of 'au::Quantity<au::Seconds,double>'
error_examples.cc(35): error C2248: 'au::Quantity<au::Meters,double>::Quantity': cannot access private member declared in class 'au::Quantity<au::Meters,double>'
D:\a\au\au\au.hh(6759): note: see declaration of 'au::Quantity<au::Meters,double>::Quantity'
D:\a\au\au\au.hh(6403): note: see declaration of 'au::Quantity<au::Meters,double>'
Input to Maker¶
Meaning: This happens when you try to pass something to a “maker” (quantity maker, or quantity
point maker), but it’s already a Quantity
or QuantityPoint
.
Solution: Generally, this is pretty easy: just remove the redundant call.
Example
Code
Compiler error (clang 14)
./au/quantity.hh:595:9: error: static_assert failed due to requirement 'is_not_already_a_quantity' "Input to QuantityMaker is already a Quantity"
static_assert(is_not_already_a_quantity, "Input to QuantityMaker is already a Quantity");
^ ~~~~~~~~~~~~~~~~~~~~~~~~~
au/error_examples.cc:48:11: note: in instantiation of function template specialization 'au::QuantityMaker<au::Meters>::operator()<au::Meters, int>' requested here
meters(x);
^
In file included from au/error_examples.cc:15:
In file included from ./au/au.hh:17:
In file included from ./au/chrono_interop.hh:20:
In file included from ./au/prefix.hh:19:
./au/quantity_point.hh:273:9: error: static_assert failed due to requirement 'is_not_already_a_quantity_point' "Input to QuantityPointMaker is already a QuantityPoint"
static_assert(is_not_already_a_quantity_point,
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
au/error_examples.cc:51:14: note: in instantiation of function template specialization 'au::QuantityPointMaker<au::Meters>::operator()<au::Meters, int>' requested here
meters_pt(x_pt);
^
In file included from au/error_examples.cc:15:
In file included from ./au/au.hh:17:
In file included from ./au/chrono_interop.hh:20:
In file included from ./au/prefix.hh:18:
Compiler error (gcc 10)
In file included from ./au/prefix.hh:18,
from ./au/chrono_interop.hh:20,
from ./au/au.hh:17,
from au/error_examples.cc:15:
./au/quantity.hh: In instantiation of 'constexpr void au::QuantityMaker<UnitT>::operator()(au::Quantity<OtherUnit, OtherRep>) const [with U = au::Meters; R = int; UnitT = au::Meters]':
au/error_examples.cc:48:13: required from here
./au/quantity.hh:595:23: error: static assertion failed: Input to QuantityMaker is already a Quantity
595 | static_assert(is_not_already_a_quantity, "Input to QuantityMaker is already a Quantity");
| ^~~~~~~~~~~~~~~~~~~~~~~~~
In file included from ./au/prefix.hh:19,
from ./au/chrono_interop.hh:20,
from ./au/au.hh:17,
from au/error_examples.cc:15:
./au/quantity_point.hh: In instantiation of 'constexpr void au::QuantityPointMaker<UnitT>::operator()(au::QuantityPoint<U, R>) const [with U = au::Meters; R = int; Unit = au::Meters]':
au/error_examples.cc:51:19: required from here
./au/quantity_point.hh:273:23: error: static assertion failed: Input to QuantityPointMaker is already a QuantityPoint
273 | static_assert(is_not_already_a_quantity_point,
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Compiler error (MSVC 2022 x64)
D:\a\au\au\au.hh(6871): error C2338: static_assert failed: 'Input to QuantityMaker is already a Quantity'
D:\a\au\au\au.hh(6871): note: the template instantiation context (the oldest one first) is
error_examples.cc(50): note: see reference to function template instantiation 'void au::QuantityMaker<au::Meters>::operator ()<au::Meters,int>(au::Quantity<au::Meters,int>) const' being compiled
error_examples.cc(50): note: see the first reference to 'au::QuantityMaker<au::Meters>::operator ()' in 'au::example_input_to_maker'
D:\a\au\au\au.hh(7880): error C2338: static_assert failed: 'Input to QuantityPointMaker is already a QuantityPoint'
D:\a\au\au\au.hh(7880): note: the template instantiation context (the oldest one first) is
error_examples.cc(53): note: see reference to function template instantiation 'void au::QuantityPointMaker<au::Meters>::operator ()<Unit,T>(au::QuantityPoint<Unit,T>) const' being compiled
with
[
Unit=au::Meters,
T=int
]
error_examples.cc(53): note: see the first reference to 'au::QuantityPointMaker<au::Meters>::operator ()' in 'au::example_input_to_maker'
Conversion risk too high¶
Meaning: This is a physically meaningful conversion, but we think the risk of a grossly incorrect answer is too high, so we forbid it. There are two main types of conversion risk, both most prominently associated with integral storage types.
-
Truncation risk. Example:
inches(24).as(feet)
. -
Overflow risk. Example:
giga(hertz)(1).as(hertz)
.
Both of these examples would in fact produce the correct answer with the specific values given (24
and 1
). However, many (most!) other values would not:
- In
inches(x).as(feet)
, forint x
, any value not exactly divisible by 12 would truncate. - In
giga(hertz)(x).as(hertz)
, forint x
, any value greater than 2 would overflow.
Thus, we disallow the entire conversion operation — at least, by default.
Solution: There are different strategies to solve this, depending on your use case.
-
Use floating point. As mentioned above, these risks only apply to integer values. If floating point is what you want anyway, just use it.
giga(hertz)(1.0).as(hertz)
produceshertz(1'000'000'000.0)
. -
Turn off the safety checks.
inches(24).as(feet, ignore(TRUNCATION_RISK))
producesfeet(2)
.
The syntax for the latter is to pass ignore(X)
as a second argument to your conversion function,
where X
is that conversion’s “risk set” — that is, the set of risks that caused Au to prevent
that conversion from compiling. The “risk set” will be one of these:
OVERFLOW_RISK
TRUNCATION_RISK
OVERFLOW_RISK | TRUNCATION_RISK
(as in, “both risks”)
The same compiler error that links directly to this section of the troubleshooting guide will tell you the risk set for your conversion, so you don’t have to guess.
Warning
Stop and think before overriding the safety checks. If you’re reviewing code that uses it, ask about it. The library is trying to protect you from an error prone operation. The mechanism exists because sometimes you can know that it’s OK, but remember to stop and check first!
Example
Code
Compiler error (clang 14)
./au/quantity.hh:467:9: error: static_assert failed due to requirement '!is_truncation_only_unacceptable_risk' "Truncation risk too high. See <https://aurora-opensource.github.io/au/main/troubleshooting/#risk-too-high>. Your \"risk set\" is `TRUNCATION_RISK`."
static_assert(!is_truncation_only_unacceptable_risk,
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
./au/quantity.hh:192:60: note: in instantiation of function template specialization 'au::Quantity<au::Inches, int>::in_impl<int, au::QuantityMaker<au::Feet>, au::detail::CheckTheseRisks<au::detail::RiskSet<'\x03'>>>' requested here
return make_quantity<AssociatedUnitT<NewUnitSlot>>(in_impl<Rep>(u, policy));
^
au/error_examples.cc:59:16: note: in instantiation of function template specialization 'au::Quantity<au::Inches, int>::as<au::QuantityMaker<au::Feet>, au::detail::CheckTheseRisks<au::detail::RiskSet<'\x03'>>>' requested here
inches(24).as(feet);
^
In file included from au/error_examples.cc:15:
In file included from ./au/au.hh:17:
In file included from ./au/chrono_interop.hh:20:
In file included from ./au/prefix.hh:18:
./au/quantity.hh:460:9: error: static_assert failed due to requirement '!is_overflow_only_unacceptable_risk' "Overflow risk too high. See <https://aurora-opensource.github.io/au/main/troubleshooting/#risk-too-high>. Your \"risk set\" is `OVERFLOW_RISK`."
static_assert(!is_overflow_only_unacceptable_risk,
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
./au/quantity.hh:192:60: note: in instantiation of function template specialization 'au::Quantity<au::Giga<au::Hertz>, int>::in_impl<int, au::QuantityMaker<au::Hertz>, au::detail::CheckTheseRisks<au::detail::RiskSet<'\x03'>>>' requested here
return make_quantity<AssociatedUnitT<NewUnitSlot>>(in_impl<Rep>(u, policy));
^
au/error_examples.cc:62:20: note: in instantiation of function template specialization 'au::Quantity<au::Giga<au::Hertz>, int>::as<au::QuantityMaker<au::Hertz>, au::detail::CheckTheseRisks<au::detail::RiskSet<'\x03'>>>' requested here
giga(hertz)(1).as(hertz);
^
Compiler error (gcc 10)
In file included from ./au/prefix.hh:18,
from ./au/chrono_interop.hh:20,
from ./au/au.hh:17,
from au/error_examples.cc:15:
./au/quantity.hh: In instantiation of 'constexpr OtherRep au::Quantity<UnitT, RepT>::in_impl(OtherUnitSlot, RiskPolicyT) const [with OtherRep = int; OtherUnitSlot = au::QuantityMaker<au::Feet>; RiskPolicyT = au::detail::CheckTheseRisks<au::detail::RiskSet<3> >; UnitT = au::Inches; RepT = int]':
./au/quantity.hh:192:72: required from 'constexpr auto au::Quantity<UnitT, RepT>::as(NewUnitSlot, RiskPolicyT) const [with NewUnitSlot = au::QuantityMaker<au::Feet>; RiskPolicyT = au::detail::CheckTheseRisks<au::detail::RiskSet<3> >; UnitT = au::Inches; RepT = int]'
au/error_examples.cc:59:23: required from here
./au/quantity.hh:467:23: error: static assertion failed: Truncation risk too high. See <https://aurora-opensource.github.io/au/main/troubleshooting/#risk-too-high>. Your "risk set" is `TRUNCATION_RISK`.
467 | static_assert(!is_truncation_only_unacceptable_risk,
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
./au/quantity.hh: In instantiation of 'constexpr OtherRep au::Quantity<UnitT, RepT>::in_impl(OtherUnitSlot, RiskPolicyT) const [with OtherRep = int; OtherUnitSlot = au::QuantityMaker<au::Hertz>; RiskPolicyT = au::detail::CheckTheseRisks<au::detail::RiskSet<3> >; UnitT = au::Giga<au::Hertz>; RepT = int]':
./au/quantity.hh:192:72: required from 'constexpr auto au::Quantity<UnitT, RepT>::as(NewUnitSlot, RiskPolicyT) const [with NewUnitSlot = au::QuantityMaker<au::Hertz>; RiskPolicyT = au::detail::CheckTheseRisks<au::detail::RiskSet<3> >; UnitT = au::Giga<au::Hertz>; RepT = int]'
au/error_examples.cc:62:28: required from here
./au/quantity.hh:460:23: error: static assertion failed: Overflow risk too high. See <https://aurora-opensource.github.io/au/main/troubleshooting/#risk-too-high>. Your "risk set" is `OVERFLOW_RISK`.
460 | static_assert(!is_overflow_only_unacceptable_risk,
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Compiler error (MSVC 2022 x64)
D:\a\au\au\au.hh(6742): error C2338: static_assert failed: 'Truncation risk too high. Can silence by passing `ignore(TRUNCATION_RISK)` as second argument, but first CAREFULLY CONSIDER whether this is really what you mean to do.'
D:\a\au\au\au.hh(6742): note: the template instantiation context (the oldest one first) is
error_examples.cc(61): note: see reference to function template instantiation 'au::Quantity<au::Feet,T> au::Quantity<au::Inches,int>::as<au::QuantityMaker<au::Feet>,au::detail::CheckTheseRisks<au::detail::RiskSet<3>>>(NewUnitSlot,RiskPolicyT) const' being compiled
with
[
T=int,
NewUnitSlot=au::QuantityMaker<au::Feet>,
RiskPolicyT=au::detail::CheckTheseRisks<au::detail::RiskSet<3>>
]
D:\a\au\au\au.hh(6469): note: see reference to function template instantiation 'OtherRep au::Quantity<au::Inches,int>::in_impl<int,NewUnitSlot,RiskPolicyT>(OtherUnitSlot,RiskPolicyT) const' being compiled
with
[
OtherRep=int,
NewUnitSlot=au::QuantityMaker<au::Feet>,
RiskPolicyT=au::detail::CheckTheseRisks<au::detail::RiskSet<3>>,
OtherUnitSlot=au::QuantityMaker<au::Feet>
]
D:\a\au\au\au.hh(6735): error C2338: static_assert failed: 'Overflow risk too high. Can silence by passing `ignore(OVERFLOW_RISK)` as second argument, but first CAREFULLY CONSIDER whether this is really what you mean to do.'
D:\a\au\au\au.hh(6735): note: the template instantiation context (the oldest one first) is
error_examples.cc(64): note: see reference to function template instantiation 'au::Quantity<au::Hertz,T> au::Quantity<au::Giga<U>,int>::as<au::QuantityMaker<au::Hertz>,au::detail::CheckTheseRisks<au::detail::RiskSet<3>>>(NewUnitSlot,RiskPolicyT) const' being compiled
with
[
T=int,
U=au::Hertz,
NewUnitSlot=au::QuantityMaker<au::Hertz>,
RiskPolicyT=au::detail::CheckTheseRisks<au::detail::RiskSet<3>>
]
D:\a\au\au\au.hh(6469): note: see reference to function template instantiation 'OtherRep au::Quantity<au::Giga<U>,int>::in_impl<int,NewUnitSlot,RiskPolicyT>(OtherUnitSlot,RiskPolicyT) const' being compiled
with
[
OtherRep=int,
U=au::Hertz,
NewUnitSlot=au::QuantityMaker<au::Hertz>,
RiskPolicyT=au::detail::CheckTheseRisks<au::detail::RiskSet<3>>,
OtherUnitSlot=au::QuantityMaker<au::Hertz>
]
No type named ‘type’ in ‘std::common_type’¶
Meaning: You probably tried to perform a “common-unit operation” (addition, subtraction, comparison) with two incompatible Quantities. Typically, this means they have different dimensions, which makes this an intrinsically meaningless operation.
Solution: Figure out what dimension you expected them to have, and which value had the wrong dimension. Then, figure out how to fix your expression so it has the right dimension.
Example
Code
Compiler error (clang 14)
In file included from au/error_examples.cc:15:
In file included from ./au/au.hh:17:
In file included from ./au/chrono_interop.hh:17:
In file included from external/llvm_14_toolchain_llvm/bin/../include/c++/v1/chrono:697:
In file included from external/llvm_14_toolchain_llvm/bin/../include/c++/v1/__chrono/calendar.h:13:
In file included from external/llvm_14_toolchain_llvm/bin/../include/c++/v1/__chrono/duration.h:14:
In file included from external/llvm_14_toolchain_llvm/bin/../include/c++/v1/limits:105:
external/llvm_14_toolchain_llvm/bin/../include/c++/v1/type_traits:2388:25: error: no type named 'type' in 'std::common_type<au::Quantity<au::Meters, int>, au::Quantity<au::Seconds, int>>'
template <class ..._Tp> using common_type_t = typename common_type<_Tp...>::type;
^~~~~
./au/quantity.hh:722:20: note: in instantiation of template type alias 'common_type_t' requested here
using C = std::common_type_t<T, U>;
^
./au/quantity.hh:769:20: note: in instantiation of function template specialization 'au::detail::using_common_type<au::Quantity<au::Meters, int>, au::Quantity<au::Seconds, int>, au::detail::Plus>' requested here
return detail::using_common_type(q1, q2, detail::plus);
^
au/error_examples.cc:70:15: note: in instantiation of function template specialization 'au::operator+<au::Meters, au::Seconds, int, int>' requested here
meters(1) + seconds(1);
^
Compiler error (gcc 10)
In file included from external/sysroot_x86_64//include/c++/10.3.0/ratio:39,
from external/sysroot_x86_64//include/c++/10.3.0/chrono:39,
from ./au/chrono_interop.hh:17,
from ./au/au.hh:17,
from au/error_examples.cc:15:
external/sysroot_x86_64//include/c++/10.3.0/type_traits: In substitution of 'template<class ... _Tp> using common_type_t = typename std::common_type::type [with _Tp = {au::Quantity<au::Meters, int>, au::Quantity<au::Seconds, int>}]':
./au/quantity.hh:722:11: required from 'constexpr auto au::detail::using_common_type(T, U, Func) [with T = au::Quantity<au::Meters, int>; U = au::Quantity<au::Seconds, int>; Func = au::detail::Plus]'
./au/quantity.hh:769:37: required from 'constexpr auto au::operator+(au::Quantity<U1, R1>, au::Quantity<U2, R2>) [with U1 = au::Meters; U2 = au::Seconds; R1 = int; R2 = int]'
au/error_examples.cc:70:26: required from here
external/sysroot_x86_64//include/c++/10.3.0/type_traits:2562:11: error: no type named 'type' in 'struct std::common_type<au::Quantity<au::Meters, int>, au::Quantity<au::Seconds, int> >'
2562 | using common_type_t = typename common_type<_Tp...>::type;
| ^~~~~~~~~~~~~
In file included from ./au/prefix.hh:18,
from ./au/chrono_interop.hh:20,
from ./au/au.hh:17,
from au/error_examples.cc:15:
./au/quantity.hh: In instantiation of 'constexpr auto au::detail::using_common_type(T, U, Func) [with T = au::Quantity<au::Meters, int>; U = au::Quantity<au::Seconds, int>; Func = au::detail::Plus]':
./au/quantity.hh:769:37: required from 'constexpr auto au::operator+(au::Quantity<U1, R1>, au::Quantity<U2, R2>) [with U1 = au::Meters; U2 = au::Seconds; R1 = int; R2 = int]'
au/error_examples.cc:70:26: required from here
./au/quantity.hh:724:94: error: no type named 'type' in 'struct std::common_type<au::Quantity<au::Meters, int>, au::Quantity<au::Seconds, int> >'
724 | std::is_same<typename C::Rep, std::common_type_t<typename T::Rep, typename U::Rep>>::value,
| ^~~~~
Compiler error (MSVC 2022 x64)
C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Tools\MSVC\14.44.35207\include\type_traits(1300): error C2794: 'type': is not a member of any direct or indirect base class of 'std::common_type<T,U>'
with
[
T=au::Quantity<au::Meters,int>,
U=au::Quantity<au::Seconds,int>
]
C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Tools\MSVC\14.44.35207\include\type_traits(1300): note: the template instantiation context (the oldest one first) is
error_examples.cc(72): note: see reference to function template instantiation 'auto au::operator +<au::Meters,au::Seconds,int,int>(au::Quantity<au::Meters,int>,au::Quantity<au::Seconds,int>)' being compiled
D:\a\au\au\au.hh(7045): note: see reference to function template instantiation 'auto au::detail::using_common_type<au::Quantity<au::Meters,int>,au::Quantity<au::Seconds,int>,au::detail::Plus>(T,U,Func)' being compiled
with
[
T=au::Quantity<au::Meters,int>,
U=au::Quantity<au::Seconds,int>,
Func=au::detail::Plus
]
D:\a\au\au\au.hh(6998): note: see reference to alias template instantiation 'std::common_type_t<T,U>' being compiled
with
[
T=au::Quantity<au::Meters,int>,
U=au::Quantity<au::Seconds,int>
]
D:\a\au\au\au.hh(6998): error C2938: 'std::common_type_t' : Failed to specialize alias template
D:\a\au\au\au.hh(7000): error C2057: expected constant expression
D:\a\au\au\au.hh(6847): error C2668: 'au::Quantity<au::Meters,int>::as': ambiguous call to overloaded function
D:\a\au\au\au.hh(6468): note: could be 'auto au::Quantity<au::Meters,int>::as<NewRep,au::detail::CheckTheseRisks<au::detail::RiskSet<3>>>(NewUnitSlot,RiskPolicyT) const'
with
[
NewRep=TargetUnit::Rep,
NewUnitSlot=TargetUnit::Rep,
RiskPolicyT=au::detail::CheckTheseRisks<au::detail::RiskSet<3>>
]
D:\a\au\au\au.hh(6462): note: or 'auto au::Quantity<au::Meters,int>::as<NewRep,Unit,au::detail::CheckTheseRisks<au::detail::RiskSet<0>>>(NewUnitSlot,RiskPolicyT) const'
with
[
NewRep=TargetUnit::Rep,
Unit=au::Meters,
NewUnitSlot=au::Meters,
RiskPolicyT=au::detail::CheckTheseRisks<au::detail::RiskSet<0>>
]
D:\a\au\au\au.hh(6847): note: while trying to match the argument list '(Unit)'
with
[
Unit=au::Meters
]
D:\a\au\au\au.hh(6847): note: the template instantiation context (the oldest one first) is
D:\a\au\au\au.hh(7003): note: see reference to function template instantiation 'auto au::detail::cast_to_common_type<au::detail::using_common_type::C,au::Meters,int>(au::Quantity<au::Meters,int>)' being compiled
D:\a\au\au\au.hh(6993): note: see reference to function template instantiation 'auto au::rep_cast<TargetUnit::Rep,au::Meters,int>(au::Quantity<au::Meters,int>)' being compiled
D:\a\au\au\au.hh(6847): error C2668: 'au::Quantity<au::Seconds,int>::as': ambiguous call to overloaded function
D:\a\au\au\au.hh(6468): note: could be 'auto au::Quantity<au::Seconds,int>::as<NewRep,au::detail::CheckTheseRisks<au::detail::RiskSet<3>>>(NewUnitSlot,RiskPolicyT) const'
with
[
NewRep=TargetUnit::Rep,
NewUnitSlot=TargetUnit::Rep,
RiskPolicyT=au::detail::CheckTheseRisks<au::detail::RiskSet<3>>
]
D:\a\au\au\au.hh(6462): note: or 'auto au::Quantity<au::Seconds,int>::as<NewRep,Unit,au::detail::CheckTheseRisks<au::detail::RiskSet<0>>>(NewUnitSlot,RiskPolicyT) const'
with
[
NewRep=TargetUnit::Rep,
Unit=au::Seconds,
NewUnitSlot=au::Seconds,
RiskPolicyT=au::detail::CheckTheseRisks<au::detail::RiskSet<0>>
]
D:\a\au\au\au.hh(6847): note: while trying to match the argument list '(Unit)'
with
[
Unit=au::Seconds
]
D:\a\au\au\au.hh(6847): note: the template instantiation context (the oldest one first) is
D:\a\au\au\au.hh(7003): note: see reference to function template instantiation 'auto au::detail::cast_to_common_type<au::detail::using_common_type::C,au::Seconds,int>(au::Quantity<au::Seconds,int>)' being compiled
D:\a\au\au\au.hh(6993): note: see reference to function template instantiation 'auto au::rep_cast<TargetUnit::Rep,au::Seconds,int>(au::Quantity<au::Seconds,int>)' being compiled
D:\a\au\au\au.hh(7003): error C3889: call to object of class type 'au::detail::Plus': no matching call operator found
D:\a\au\au\au.hh(1361): note: could be 'auto au::detail::Plus::operator ()(const T &,const U &) const'
D:\a\au\au\au.hh(7003): note: Failed to specialize function template 'auto au::detail::Plus::operator ()(const T &,const U &) const'
D:\a\au\au\au.hh(7003): note: With the following template arguments:
D:\a\au\au\au.hh(7003): note: 'T=void'
D:\a\au\au\au.hh(7003): note: 'U=void'
D:\a\au\au\au.hh(7003): note: you cannot create a reference to 'void'
Can’t pass Quantity
to a unit slot¶
Other variants:
- “Can’t pass
QuantityPoint
to a unit slot” - “Can’t pass
Quantity
to a unit slot for points” - “Can’t pass
QuantityPoint
to a unit slot for points”
Meaning: A unit slot is an API that takes any unit-named
type in the library, and treats it as the associated unit. Besides simple unit types themselves,
these can include quantity makers (such as meters
), unit symbols (such as symbols::m
), constants
(such as SPEED_OF_LIGHT
), and so on.
Notably, what it cannot include is a Quantity
or QuantityPoint
. Notice that all of the types
we mentioned above have a completely unambiguous value, known at compile time from the type
alone. This is not the case for something like Quantity
, which holds an underlying runtime
numeric value, to represent the quantity in its specific unit.
Solution: If you’re attempting to use the Quantity
as an ad hoc unit, simply replace it with
a unit that you scale by a magnitude, mag<N>()
.
Example
Code
Let’s try to round a quantity of bytes to the nearest 10-byte amount.
Compiler error (clang 14)
In file included from au/error_examples.cc:15:
In file included from ./au/au.hh:17:
In file included from ./au/chrono_interop.hh:20:
In file included from ./au/prefix.hh:18:
./au/quantity.hh:491:5: error: static_assert failed due to requirement 'detail::AlwaysFalse<au::Bytes, int>::value' "Can't pass `Quantity` to a unit slot (see: https://aurora-opensource.github.io/au/main/troubleshooting/#quantity-to-unit-slot)"
static_assert(
^
./au/unit_of_measure.hh:145:1: note: in instantiation of template class 'au::AssociatedUnit<au::Quantity<au::Bytes, int>>' requested here
using AssociatedUnitT = typename AssociatedUnit<U>::type;
^
./au/math.hh:497:26: note: in instantiation of template type alias 'AssociatedUnitT' requested here
return make_quantity<AssociatedUnitT<RoundingUnits>>(round_in<OutputRep>(rounding_units, q));
^
au/error_examples.cc:78:12: note: in instantiation of function template specialization 'au::round_as<int, au::Quantity<au::Bytes, int>, au::Bytes, int>' requested here
size = round_as<int>(bytes(10), size);
^
Compiler error (gcc 10)
In file included from ./au/prefix.hh:18,
from ./au/chrono_interop.hh:20,
from ./au/au.hh:17,
from au/error_examples.cc:15:
./au/quantity.hh: In instantiation of 'struct au::AssociatedUnit<au::Quantity<au::Bytes, int> >':
./au/unit_of_measure.hh:145:7: required by substitution of 'template<class U> using AssociatedUnitT = typename au::AssociatedUnit::type [with U = au::Quantity<au::Bytes, int>]'
./au/math.hh:497:12: required from 'auto au::round_as(RoundingUnits, au::Quantity<U2, R2>) [with OutputRep = int; RoundingUnits = au::Quantity<au::Bytes, int>; U = au::Bytes; R = int]'
au/error_examples.cc:78:41: required from here
./au/quantity.hh:492:36: error: static assertion failed: Can't pass `Quantity` to a unit slot (see: https://aurora-opensource.github.io/au/main/troubleshooting/#quantity-to-unit-slot)
492 | detail::AlwaysFalse<U, R>::value,
| ^~~~~
In file included from ./au/conversion_policy.hh:26,
from ./au/quantity.hh:19,
from ./au/prefix.hh:18,
from ./au/chrono_interop.hh:20,
from ./au/au.hh:17,
from au/error_examples.cc:15:
./au/unit_of_measure.hh: In substitution of 'template<class U> using AssociatedUnitT = typename au::AssociatedUnit::type [with U = au::Quantity<au::Bytes, int>]':
./au/math.hh:497:12: required from 'auto au::round_as(RoundingUnits, au::Quantity<U2, R2>) [with OutputRep = int; RoundingUnits = au::Quantity<au::Bytes, int>; U = au::Bytes; R = int]'
au/error_examples.cc:78:41: required from here
./au/unit_of_measure.hh:145:7: error: no type named 'type' in 'struct au::AssociatedUnit<au::Quantity<au::Bytes, int> >'
145 | using AssociatedUnitT = typename AssociatedUnit<U>::type;
| ^~~~~~~~~~~~~~~
In file included from ./au/prefix.hh:18,
from ./au/chrono_interop.hh:20,
from ./au/au.hh:17,
from au/error_examples.cc:15:
./au/quantity.hh: In instantiation of 'constexpr OtherRep au::Quantity<UnitT, RepT>::in_impl(OtherUnitSlot, RiskPolicyT) const [with OtherRep = double; OtherUnitSlot = au::Quantity<au::Bytes, int>; RiskPolicyT = au::detail::CheckTheseRisks<au::detail::RiskSet<0> >; UnitT = au::Bytes; RepT = int]':
./au/quantity.hh:200:31: required from 'constexpr auto au::Quantity<UnitT, RepT>::in(NewUnitSlot, RiskPolicyT) const [with NewRep = double; NewUnitSlot = au::Quantity<au::Bytes, int>; RiskPolicyT = au::detail::CheckTheseRisks<au::detail::RiskSet<0> >; UnitT = au::Bytes; RepT = int]'
./au/math.hh:445:52: required from 'auto au::round_in(RoundingUnits, au::Quantity<Unit, Rep>) [with RoundingUnits = au::Quantity<au::Bytes, int>; U = au::Bytes; R = int]'
./au/math.hh:463:43: required from 'auto au::round_in(RoundingUnits, au::Quantity<U2, R2>) [with OutputRep = int; RoundingUnits = au::Quantity<au::Bytes, int>; U = au::Bytes; R = int]'
./au/math.hh:497:77: required from 'auto au::round_as(RoundingUnits, au::Quantity<U2, R2>) [with OutputRep = int; RoundingUnits = au::Quantity<au::Bytes, int>; U = au::Bytes; R = int]'
au/error_examples.cc:78:41: required from here
./au/quantity.hh:446:42: error: no type named 'type' in 'struct au::AssociatedUnit<au::Quantity<au::Bytes, int> >'
446 | static_assert(IsUnit<OtherUnit>::value, "Invalid type passed to unit slot");
| ^~~~~
./au/quantity.hh:448:15: error: no type named 'type' in 'struct au::AssociatedUnit<au::Quantity<au::Bytes, int> >'
448 | using Op = detail::ConversionForRepsAndFactor<Rep, OtherRep, UnitRatioT<Unit, OtherUnit>>;
| ^~
./au/quantity.hh:452:85: error: no type named 'type' in 'struct au::AssociatedUnit<au::Quantity<au::Bytes, int> >'
452 | constexpr bool is_overflow_risk_ok = detail::OverflowRiskAcceptablyLow<Op>::value;
| ^~~~~
./au/quantity.hh:456:89: error: no type named 'type' in 'struct au::AssociatedUnit<au::Quantity<au::Bytes, int> >'
456 | constexpr bool is_truncation_risk_ok = detail::TruncationRiskAcceptablyLow<Op>::value;
| ^~~~~
Compiler error (MSVC 2022 x64)
D:\a\au\au\au.hh(6768): error C2338: static_assert failed: 'Can't pass `Quantity` to a unit slot (see: https://aurora-opensource.github.io/au/main/troubleshooting/#quantity-to-unit-slot)'
D:\a\au\au\au.hh(6768): note: the template instantiation context (the oldest one first) is
error_examples.cc(80): note: see reference to function template instantiation 'auto au::round_as<int,au::Quantity<au::Bytes,int>,au::Bytes,int>(RoundingUnits,au::Quantity<au::Bytes,int>)' being compiled
with
[
RoundingUnits=au::Quantity<au::Bytes,int>
]
D:\a\au\au\au.hh(3638): note: see reference to class template instantiation 'au::AssociatedUnit<RoundingUnits>' being compiled
with
[
RoundingUnits=au::Quantity<au::Bytes,int>
]
D:\a\au\au\au.hh(3638): error C2794: 'type': is not a member of any direct or indirect base class of 'au::AssociatedUnit<RoundingUnits>'
with
[
RoundingUnits=au::Quantity<au::Bytes,int>
]
D:\a\au\au\au.hh(7394): error C2938: 'au::AssociatedUnitT' : Failed to specialize alias template
D:\a\au\au\au.hh(7342): error C2672: 'au::Quantity<au::Bytes,int>::in': no matching overloaded function found
D:\a\au\au\au.hh(5020): note: could be 'int au::Quantity<au::Bytes,int>::in(NewUnit) const'
D:\a\au\au\au.hh(7342): note: 'int au::Quantity<au::Bytes,int>::in(NewUnit) const': could not deduce template argument for '<unnamed-symbol>'
D:\a\au\au\au.hh(5019): note: 'std::enable_if_t<false,void>' : Failed to specialize alias template
D:\a\au\au\au.hh(5010): note: or 'NewRep au::Quantity<au::Bytes,int>::in(NewUnit) const'
D:\a\au\au\au.hh(7342): note: 'NewRep au::Quantity<au::Bytes,int>::in(NewUnit) const': could not deduce template argument for '<unnamed-symbol>'
D:\a\au\au\au.hh(5009): note: 'au::AssociatedUnitT' : Failed to specialize alias template
D:\a\au\au\au.hh(3638): note: 'type': is not a member of any direct or indirect base class of 'au::AssociatedUnit<RoundingUnits>'
with
[
RoundingUnits=au::Quantity<au::Bytes,int>
]
D:\a\au\au\au.hh(5009): note: syntax error: missing '>' before identifier '<missingId>'
D:\a\au\au\au.hh(7342): note: the template instantiation context (the oldest one first) is
D:\a\au\au\au.hh(7394): note: see reference to function template instantiation 'auto au::round_in<OutputRep,RoundingUnits,au::Bytes,int>(RoundingUnits,au::Quantity<au::Bytes,int>)' being compiled
with
[
OutputRep=int,
RoundingUnits=au::Quantity<au::Bytes,int>
]
D:\a\au\au\au.hh(7360): note: see reference to function template instantiation 'auto au::round_in<RoundingUnits,au::Bytes,int>(RoundingUnits,au::Quantity<au::Bytes,int>)' being compiled
with
[
RoundingUnits=au::Quantity<au::Bytes,int>
]
D:\a\au\au\au.hh(7360): error C2440: 'static_cast': cannot convert from 'void' to 'OutputRep'
with
[
OutputRep=int
]
D:\a\au\au\au.hh(7360): note: Expressions of type void cannot be converted to other types
D:\a\au\au\au.hh(7394): error C2672: 'au::make_quantity': no matching overloaded function found
D:\a\au\au\au.hh(4872): note: could be 'auto au::make_quantity(T)'
D:\a\au\au\au.hh(7394): note: Failed to specialize function template 'auto au::make_quantity(T)'
D:\a\au\au\au.hh(7394): note: With the following template arguments:
D:\a\au\au\au.hh(7394): note: 'UnitT=unknown-type'
D:\a\au\au\au.hh(7394): note: 'T=void'
D:\a\au\au\au.hh(7394): note: 'void' cannot be used as a function parameter except for '(void)'
error_examples.cc(79): error C2679: binary '=': no operator found which takes a right-hand operand of type 'void' (or there is no acceptable conversion)
D:\a\au\au\au.hh(5261): note: could be 'au::Quantity<au::Bytes,int> &au::Quantity<au::Bytes,int>::operator =(au::Quantity<au::Bytes,int> &&)'
error_examples.cc(79): note: 'au::Quantity<au::Bytes,int> &au::Quantity<au::Bytes,int>::operator =(au::Quantity<au::Bytes,int> &&)': cannot convert argument 2 from 'void' to 'au::Quantity<au::Bytes,int> &&'
error_examples.cc(79): note: Expressions of type void cannot be converted to other types
D:\a\au\au\au.hh(5261): note: or 'au::Quantity<au::Bytes,int> &au::Quantity<au::Bytes,int>::operator =(const au::Quantity<au::Bytes,int> &)'
error_examples.cc(79): note: 'au::Quantity<au::Bytes,int> &au::Quantity<au::Bytes,int>::operator =(const au::Quantity<au::Bytes,int> &)': cannot convert argument 2 from 'void' to 'const au::Quantity<au::Bytes,int> &'
error_examples.cc(79): note: Expressions of type void cannot be converted to other types
error_examples.cc(79): note: while trying to match the argument list '(au::Quantity<au::Bytes,int>, void)'
Integer division forbidden¶
Meaning: Although Au generally tries to act just like the underlying raw numeric types, we also try to prevent wrong code that looks correct from compiling. Over time, we have found that certain instances of integer division — namely, cases where the denominator has units, and those units are different from the numerator — are extremely pernicious. The code looks correct at first glance even to most experts, but the risk for truncating (even down to zero!) can be much higher than usual.
Solution: Floating point types do not have this problem, so you can change either of the
variables to floating point. If you cannot use floating point, then wrapping the denominator in
unblock_int_div()
will overrule the compiler error. However, please be very careful about this
approach: read the warning below first.
Carefully consider your situation before using unblock_int_div()
Au already accepts many common integer division use cases without complaint, including:
- Dividing by a raw numeric integer, not a
Quantity
-
Dividing by a
Quantity
whose units are the same as the numerator
Thus, if you see this error, it is probably preventing a serious mistake.
Let’s revisit the example from the unblock_int_div
docs, and consider the effect of different units in
the denominator. (We will assign to a quantity of miles per hour for illustration purposes.)
// `v1`: Original example above.
Quantity<UnitQuotientT<Miles, Hours>> v1
= miles(115) / unblock_int_div(hours(2));
// `v2`: Changing the denominator's units.
Quantity<UnitQuotientT<Miles, Hours>> v2
= miles(115) / unblock_int_div(minutes(120));
v1
and v2
represent the same quantity, in principle. However, v1
has a value of
(miles / hour)(57)
, while v2
has a value of (miles / hour)(0)
!
We hope this sobering example helps to communicate the risk, and encourages you to think
carefully before using unblock_int_div
. If you still find that it’s appropriate for your use
case, we suggest including a brief comment to explain why for the benefit of future readers.
Example
Code
How long does it take to travel 60 m at a speed of 65 MPH?
Compiler error (clang 14)
In file included from au/error_examples.cc:15:
In file included from ./au/au.hh:17:
In file included from ./au/chrono_interop.hh:20:
In file included from ./au/prefix.hh:18:
./au/quantity.hh:436:9: error: static_assert failed due to requirement 'are_units_quantity_equivalent || !uses_integer_division' "Dangerous integer division forbidden. See <https://aurora-opensource.github.io/au/main/troubleshooting/#integer-division-forbidden> for more details about the risks, and your options to resolve this error."
static_assert(are_units_quantity_equivalent || !uses_integer_division,
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
./au/quantity.hh:309:9: note: in instantiation of function template specialization 'au::Quantity<au::Meters, int>::warn_if_integer_division<au::UnitProduct<au::Miles, au::Pow<au::Hours, -1>>, int>' requested here
warn_if_integer_division<OtherUnit, OtherRep>();
^
au/error_examples.cc:86:39: note: in instantiation of function template specialization 'au::Quantity<au::Meters, int>::operator/<au::UnitProduct<au::Miles, au::Pow<au::Hours, -1>>, int>' requested here
QuantityD<Seconds> t = meters(60) / (miles / hour)(65);
^
Compiler error (gcc 10)
In file included from ./au/prefix.hh:18,
from ./au/chrono_interop.hh:20,
from ./au/au.hh:17,
from au/error_examples.cc:15:
./au/quantity.hh: In instantiation of 'static constexpr void au::Quantity<UnitT, RepT>::warn_if_integer_division() [with OtherUnit = au::UnitProduct<au::Miles, au::Pow<au::Hours, -1> >; OtherRep = int; UnitT = au::Meters; RepT = int]':
./au/quantity.hh:309:54: required from here
au/error_examples.cc:86:58: in 'constexpr' expansion of 'au::meters.au::QuantityMaker<au::Meters>::operator()<int>(60).au::Quantity<au::Meters, int>::operator/<au::UnitProduct<au::Miles, au::Pow<au::Hours, -1> >, int>(au::miles.au::QuantityMaker<au::Miles>::operator/<au::Hours>((au::hour, const au::SingularNameFor<au::Hours>())).au::QuantityMaker<au::UnitProduct<au::Miles, au::Pow<au::Hours, -1> > >::operator()<int>(65))'
./au/quantity.hh:436:53: error: static assertion failed: Dangerous integer division forbidden. See <https://aurora-opensource.github.io/au/main/troubleshooting/#integer-division-forbidden> for more details about the risks, and your options to resolve this error.
436 | static_assert(are_units_quantity_equivalent || !uses_integer_division,
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~
Compiler error (MSVC 2022 x64)
D:\a\au\au\au.hh(6713): error C2338: static_assert failed: 'Integer division forbidden: wrap denominator in `unblock_int_div()` if you really want it'
D:\a\au\au\au.hh(6713): note: the template instantiation context (the oldest one first) is
error_examples.cc(88): note: see reference to function template instantiation 'au::Quantity<au::UnitProduct<T,au::Pow<B,-1>,au::Hours>,int> au::Quantity<au::Meters,int>::operator /<au::UnitProduct<au::Miles,au::Pow<au::Hours,-1>>,int>(au::Quantity<au::UnitProduct<au::Miles,au::Pow<au::Hours,-1>>,int>) const' being compiled
with
[
T=au::Meters,
B=au::Miles
]
D:\a\au\au\au.hh(6586): note: see reference to function template instantiation 'void au::Quantity<au::Meters,int>::warn_if_integer_division<OtherUnit,OtherRep>(void)' being compiled
with
[
OtherUnit=au::UnitProduct<au::Miles,au::Pow<au::Hours,-1>>,
OtherRep=int
]
Dangerous inversion¶
Meaning: This is analogous to our overflow safety surface. When computing the inverse of an integral quantity in a given target unit, there is some smallest value that will be “lossy”: that is, where converting back to the original unit will not be guaranteed to produce the original value. Au’s policy is that all values up to at least 1,000 must be free from this kind of loss: otherwise, we forbid this operation.
Solution: Consider using floating point; you’ll always get a precise answer. Alternatively, use a smaller target unit: one whose product with the input unit is smaller than \left(\frac{1}{1,000}\right)^2 = \frac{1}{1,000,000}.
Example
Code
Note
If you’re really sure it’s OK, you can use the explicit-Rep version of inverse_as
, which is
forcing like a static_cast
. This is rarely the right choice, though. Consider:
This yields seconds(0)
, due to the gross truncation error which the check was designed to
prevent in the first place.
Compiler error (clang 14)
In file included from au/error_examples.cc:15:
In file included from ./au/au.hh:19:
./au/math.hh:272:5: error: static_assert failed due to requirement 'UNITY.in<int>(associated_unit(au::QuantityMaker<au::Seconds>{}) * au::Hertz{}) >= threshold || std::is_floating_point<int>::value' "Dangerous inversion risking truncation to 0; must supply explicit Rep if truly desired"
static_assert(
^
./au/math.hh:288:56: note: in instantiation of function template specialization 'au::inverse_in<au::QuantityMaker<au::Seconds>, au::Hertz, int>' requested here
return make_quantity<AssociatedUnitT<TargetUnits>>(inverse_in(target_units, q));
^
au/error_examples.cc:94:5: note: in instantiation of function template specialization 'au::inverse_as<au::QuantityMaker<au::Seconds>, au::Hertz, int>' requested here
inverse_as(seconds, hertz(5));
^
Compiler error (gcc 10)
In file included from ./au/au.hh:19,
from au/error_examples.cc:15:
./au/math.hh: In instantiation of 'constexpr auto au::inverse_in(TargetUnits, au::Quantity<Unit, Rep>) [with TargetUnits = au::QuantityMaker<au::Seconds>; U = au::Hertz; R = int]':
./au/math.hh:288:66: required from 'constexpr auto au::inverse_as(TargetUnits, au::Quantity<Unit, Rep>) [with TargetUnits = au::QuantityMaker<au::Seconds>; U = au::Hertz; R = int]'
au/error_examples.cc:94:33: required from here
./au/math.hh:273:72: error: static assertion failed: Dangerous inversion risking truncation to 0; must supply explicit Rep if truly desired
273 | UNITY.in<R>(associated_unit(TargetUnits{}) * U{}) >= threshold ||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~
274 | std::is_floating_point<R>::value,
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Compiler error (MSVC 2022 x64)
D:\a\au\au\au.hh(8856): error C2338: static_assert failed: 'Dangerous inversion risking truncation to 0; must supply explicit Rep if truly desired'
D:\a\au\au\au.hh(8856): note: the template instantiation context (the oldest one first) is
error_examples.cc(96): note: see reference to function template instantiation 'auto au::inverse_as<au::QuantityMaker<au::Seconds>,au::Hertz,int>(TargetUnits,au::Quantity<au::Hertz,int>)' being compiled
with
[
TargetUnits=au::QuantityMaker<au::Seconds>
]
D:\a\au\au\au.hh(8871): note: see reference to function template instantiation 'auto au::inverse_in<TargetUnits,au::Hertz,int>(TargetUnits,au::Quantity<au::Hertz,int>)' being compiled
with
[
TargetUnits=au::QuantityMaker<au::Seconds>
]
Deduced conflicting types¶
Meaning: In some contexts, it’s not enough to have Quantity types that can easily convert to
each other. (Common examples include the ternary operator ?:
, and initializer lists.) You need
types that are identical, or very nearly so. Even fully quantity-equivalent types, such as
\text{Hz} and \text{s}^{-1}, often won’t work in these contexts!
Solution: You can always cast non-conforming instances to your favored unit, using .as()
.
For the initializer list case, you can also make an explicit container, which will handle the
casting automatically when possible.
Example
Code
Compiler error (clang 14)
au/error_examples.cc:102:34: error: deduced conflicting types ('Quantity<au::QuantityMaker<au::Hertz>::Unit, [...]>' vs 'Quantity<au::QuantityMaker<au::Pow<au::Seconds, -1>>::Unit, [...]>') for initializer list element type
for (const auto &frequency : {
^
Compiler error (gcc 10)
au/error_examples.cc: In function 'void au::example_deduced_conflicting_types()':
au/error_examples.cc:105:10: error: unable to deduce 'std::initializer_list<auto>&&' from '{au::hertz.au::QuantityMaker<au::Hertz>::operator()<double>(1.0e+0), au::operator/<int>(1, au::seconds.au::QuantityMaker<au::Seconds>::operator()<double>(2.0e+0))}'
105 | }) {
| ^
au/error_examples.cc:105:10: note: deduced conflicting types for parameter 'auto' ('au::Quantity<au::Hertz, double>' and 'au::Quantity<au::Pow<au::Seconds, -1>, double>')
Compiler error (MSVC 2022 x64)
error_examples.cc(104): error C3535: cannot deduce type for 'auto &&' from 'initializer list'
error_examples.cc(104): error C2440: 'initializing': cannot convert from 'initializer list' to 'std::initializer_list<<error type>> &&'
error_examples.cc(104): note: Reason: cannot convert from 'initializer list' to 'std::initializer_list<<error type>>'
error_examples.cc(104): note: Element '1': no conversion from 'au::Quantity<au::Hertz,double>' to '<error type>'
error_examples.cc(104): note: Element '2': no conversion from 'au::Quantity<au::Pow<B,-1>,T>' to '<error type>'
with
[
B=au::Seconds,
T=double
]
Broken strict total ordering¶
Meaning: This means you performed an operation that needs to put unit types into a parameter pack — say, a common unit, or a unit product — but the library couldn’t figure out how to order the units inside that pack.
If that sounds obscure, it is: ordering units inside packs is a deep library implementation detail,
and we try to avoid letting end users encounter this. To reach this error, you need two distinct
units that have the same Dimension, Magnitude, and Origin. That’s a necessary but not
sufficient condition: for example, even UnitInverseT<Seconds>
and Hertz
won’t trigger this!
More background info on why this error exists
In case you want to understand more, here is the gist.
Au is heavily based on parameter packs. Some of these packs,
such as UnitProduct<...>
and CommonUnit<...>
, take units as their arguments.
Every parameter pack needs an unambiguous canonical ordering for any possible set of input arguments. Therefore, we need to create a strict total ordering for the (infinitely many!) unit types that could appear in these packs. This ordering needs to be known at compile time. The ordering itself doesn’t matter so much, but if we don’t strictly adhere to some ordering, it’s undefined behaviour.
Our strategy is to construct a “gauntlet” of properties which we can measure for any unit (e.g., Dimension, Magnitude, …), and define some arbitrary ordering for each property. We then compare the units on each property in turn. The first one where they differ “wins”. If we get through all the properties, and they’re still tied, then we have two distinct unit types which compare as equal. This would be undefined behaviour! Rather than silently ignoring this, we manifest this as a compiler error.
That is what “broken strict total ordering” means.
Solution: If you have two distinct units, and the library can’t figure out how to order them,
you can force a particular ordering. Choose one of the units U
, and give it a “tiebreaker” by
creating a specialization of ::au::UnitOrderTiebreaker<U>
. As the name suggests, this will break
the tie, and your program will compile.
Again, this is pretty unusual. For most normal ways of forming units, the library should automatically be able to define an ordering for them. If you do hit this error, it may be worth pausing to double-check that you’re using the library correctly. If you’ve checked, and it still seems like something the library should be able to handle, feel free to file an issue — maybe there’s a way we can improve our ordering!
Tip
If you hit this error, you might be annoyed by its obscurity. Instead, try feeling relieved! After all, the alternative is not “correctly working program”, but “silent undefined behaviour”. A compiler error with a searchable error message is infinitely preferable to the latter.
Example
Code
Note that this example is somewhat convoluted, but again, that’s to be expected because this error is pretty hard to hit in practice.
struct Quarterfeet : decltype(Feet{} / mag<4>()) {};
constexpr auto quarterfeet = QuantityMaker<Quarterfeet>{};
struct Trinches : decltype(Inches{} * mag<3>()) {};
constexpr auto trinches = QuantityMaker<Trinches>{};
// (BROKEN): Can't tell how to order Quarterfeet and Trinches when forming common type
if (quarterfeet(10) == trinches(10)) {
// ...
}
struct Quarterfeet : decltype(Feet{} / mag<4>()) {};
constexpr auto quarterfeet = QuantityMaker<Quarterfeet>{};
struct Trinches : decltype(Inches{} * mag<3>()) {};
constexpr auto trinches = QuantityMaker<Trinches>{};
namespace au {
template <>
struct UnitOrderTiebreaker<::Trinches> : std::integral_constant<int, 100> {};
}
// (FIXED): Trinches is no longer "tied" with Quarterfeet
if (quarterfeet(10) == trinches(10)) {
// ...
}
Compiler error (clang 14)
In file included from au/error_examples.cc:15:
In file included from ./au/au.hh:17:
In file included from ./au/chrono_interop.hh:20:
In file included from ./au/prefix.hh:18:
In file included from ./au/quantity.hh:19:
In file included from ./au/conversion_policy.hh:19:
In file included from ./au/conversion_strategy.hh:17:
In file included from ./au/abstract_operations.hh:17:
In file included from ./au/magnitude.hh:21:
./au/packs.hh:302:5: error: static_assert failed due to requirement 'std::is_same<au::Quarterfeet, au::Trinches>::value' "Broken strict total ordering: distinct input types compare equal"
static_assert(std::is_same<A, B>::value,
^ ~~~~~~~~~~~~~~~~~~~~~~~~~
./au/packs.hh:318:5: note: in instantiation of template class 'au::LexicographicTotalOrdering<au::Quarterfeet, au::Trinches>' requested here
std::conditional_t<
^
./au/packs.hh:318:5: note: in instantiation of template class 'au::LexicographicTotalOrdering<au::Quarterfeet, au::Trinches, au::detail::OrderByUnitOrderTiebreaker>' requested here
./au/packs.hh:318:5: note: in instantiation of template class 'au::LexicographicTotalOrdering<au::Quarterfeet, au::Trinches, au::detail::OrderAsOriginDisplacementUnit, au::detail::OrderByUnitOrderTiebreaker>' requested here
./au/packs.hh:318:5: note: in instantiation of template class 'au::LexicographicTotalOrdering<au::Quarterfeet, au::Trinches, au::detail::OrderAsUnitProduct, au::detail::OrderAsOriginDisplacementUnit, au::detail::OrderByUnitOrderTiebreaker>' requested here
./au/packs.hh:318:5: note: in instantiation of template class 'au::LexicographicTotalOrdering<au::Quarterfeet, au::Trinches, au::detail::OrderByOrigin, au::detail::OrderAsUnitProduct, au::detail::OrderAsOriginDisplacementUnit, au::detail::OrderByUnitOrderTiebreaker>' requested here
./au/packs.hh:318:5: note: (skipping 8 contexts in backtrace; use -ftemplate-backtrace-limit=0 to see all)
./au/unit_of_measure.hh:759:28: note: in instantiation of template class 'au::ComputeCommonUnitImpl<au::Quarterfeet, au::Trinches>' requested here
typename detail::IncludeInPackIf<IsNonzero, ComputeCommonUnitImpl, Us...>::type>::type>> {
^
./au/unit_of_measure.hh:167:1: note: in instantiation of template class 'au::ComputeCommonUnit<au::Quarterfeet, au::Trinches>' requested here
using CommonUnitT = typename ComputeCommonUnit<Us...>::type;
^
./au/quantity.hh:732:15: note: in instantiation of template type alias 'CommonUnitT' requested here
using U = CommonUnitT<U1, U2>;
^
./au/quantity.hh:743:20: note: in instantiation of function template specialization 'au::detail::convert_and_compare<au::detail::Equal, au::Quarterfeet, au::Trinches, int, int>' requested here
return detail::convert_and_compare<detail::Equal>(q1, q2);
^
au/error_examples.cc:121:25: note: in instantiation of function template specialization 'au::operator==<au::Quarterfeet, au::Trinches, int, int>' requested here
if (quarterfeet(10) == trinches(10)) {
^
In file included from au/error_examples.cc:15:
In file included from ./au/au.hh:17:
In file included from ./au/chrono_interop.hh:20:
In file included from ./au/prefix.hh:18:
In file included from ./au/quantity.hh:19:
In file included from ./au/conversion_policy.hh:19:
In file included from ./au/conversion_strategy.hh:17:
In file included from ./au/abstract_operations.hh:17:
./au/magnitude.hh:163:1: error: implicit instantiation of undefined template 'au::SignImpl<void>'
using Sign = typename SignImpl<MagT>::type;
^
./au/unit_of_measure.hh:140:1: note: in instantiation of template type alias 'Sign' requested here
using UnitSign = Sign<detail::MagT<U>>;
^
./au/quantity.hh:735:40: note: in instantiation of template type alias 'UnitSign' requested here
return detail::SignAwareComparison<UnitSign<U>, Op>{}(q1.template in<ComRep1>(U{}),
^
./au/quantity.hh:743:20: note: in instantiation of function template specialization 'au::detail::convert_and_compare<au::detail::Equal, au::Quarterfeet, au::Trinches, int, int>' requested here
return detail::convert_and_compare<detail::Equal>(q1, q2);
^
au/error_examples.cc:121:25: note: in instantiation of function template specialization 'au::operator==<au::Quarterfeet, au::Trinches, int, int>' requested here
if (quarterfeet(10) == trinches(10)) {
^
./au/magnitude.hh:161:8: note: template is declared here
struct SignImpl;
^
Compiler error (gcc 10)
In file included from ./au/magnitude.hh:21,
from ./au/abstract_operations.hh:17,
from ./au/conversion_strategy.hh:17,
from ./au/conversion_policy.hh:19,
from ./au/quantity.hh:19,
from ./au/prefix.hh:18,
from ./au/chrono_interop.hh:20,
from ./au/au.hh:17,
from au/error_examples.cc:15:
./au/packs.hh: In instantiation of 'struct au::LexicographicTotalOrdering<au::Quarterfeet, au::Trinches>':
./au/packs.hh:313:8: recursively required from 'struct au::LexicographicTotalOrdering<au::Quarterfeet, au::Trinches, au::detail::OrderByDim, au::detail::OrderByMag, au::detail::OrderByScaleFactor, au::detail::OrderByOrigin, au::detail::OrderAsUnitProduct, au::detail::OrderAsOriginDisplacementUnit, au::detail::OrderByUnitOrderTiebreaker>'
./au/packs.hh:313:8: required from 'struct au::LexicographicTotalOrdering<au::Quarterfeet, au::Trinches, au::detail::OrderByCoarseUnitOrdering, au::detail::OrderByDim, au::detail::OrderByMag, au::detail::OrderByScaleFactor, au::detail::OrderByOrigin, au::detail::OrderAsUnitProduct, au::detail::OrderAsOriginDisplacementUnit, au::detail::OrderByUnitOrderTiebreaker>'
./au/unit_of_measure.hh:1184:8: required from 'struct au::InOrderFor<au::UnitProduct, au::Quarterfeet, au::Trinches>'
./au/unit_of_measure.hh:595:8: required from 'struct au::InOrderFor<au::CommonUnit, au::Quarterfeet, au::Trinches>'
./au/packs.hh:436:8: required from 'struct au::FlatDedupedTypeList<au::CommonUnit, au::CommonUnit<au::Quarterfeet>, au::CommonUnit<au::Trinches> >'
./au/unit_of_measure.hh:745:8: required from 'struct au::ComputeCommonUnitImpl<au::Quarterfeet, au::Trinches>'
./au/unit_of_measure.hh:756:8: required from 'struct au::ComputeCommonUnit<au::Quarterfeet, au::Trinches>'
./au/unit_of_measure.hh:167:7: required by substitution of 'template<class ... Us> using CommonUnitT = typename au::ComputeCommonUnit::type [with Us = {au::Quarterfeet, au::Trinches}]'
./au/quantity.hh:732:11: required from 'constexpr auto au::detail::convert_and_compare(au::Quantity<U2, R2>, au::Quantity<U2, R2>) [with Op = au::detail::Equal; U1 = au::Quarterfeet; U2 = au::Trinches; R1 = int; R2 = int]'
./au/quantity.hh:743:54: required from 'constexpr bool au::operator==(au::Quantity<U1, R1>, au::Quantity<U2, R2>) [with U1 = au::Quarterfeet; U2 = au::Trinches; R1 = int; R2 = int]'
au/error_examples.cc:121:39: required from here
./au/packs.hh:302:39: error: static assertion failed: Broken strict total ordering: distinct input types compare equal
302 | static_assert(std::is_same<A, B>::value,
| ^~~~~
./au/packs.hh: In instantiation of 'struct au::LexicographicTotalOrdering<au::Trinches, au::Quarterfeet>':
./au/packs.hh:313:8: recursively required from 'struct au::LexicographicTotalOrdering<au::Trinches, au::Quarterfeet, au::detail::OrderByDim, au::detail::OrderByMag, au::detail::OrderByScaleFactor, au::detail::OrderByOrigin, au::detail::OrderAsUnitProduct, au::detail::OrderAsOriginDisplacementUnit, au::detail::OrderByUnitOrderTiebreaker>'
./au/packs.hh:313:8: required from 'struct au::LexicographicTotalOrdering<au::Trinches, au::Quarterfeet, au::detail::OrderByCoarseUnitOrdering, au::detail::OrderByDim, au::detail::OrderByMag, au::detail::OrderByScaleFactor, au::detail::OrderByOrigin, au::detail::OrderAsUnitProduct, au::detail::OrderAsOriginDisplacementUnit, au::detail::OrderByUnitOrderTiebreaker>'
./au/unit_of_measure.hh:1184:8: required from 'struct au::InOrderFor<au::UnitProduct, au::Trinches, au::Quarterfeet>'
./au/unit_of_measure.hh:595:8: required from 'struct au::InOrderFor<au::CommonUnit, au::Trinches, au::Quarterfeet>'
./au/unit_of_measure.hh:649:8: required from 'struct au::detail::IsFirstUnitRedundant<au::CommonUnit, au::Quarterfeet, au::Trinches>'
./au/unit_of_measure.hh:660:8: required from 'struct au::detail::EliminateRedundantUnitsImpl<au::CommonUnit<au::Trinches, au::Quarterfeet> >'
./au/unit_of_measure.hh:745:8: required from 'struct au::ComputeCommonUnitImpl<au::Quarterfeet, au::Trinches>'
./au/unit_of_measure.hh:756:8: required from 'struct au::ComputeCommonUnit<au::Quarterfeet, au::Trinches>'
./au/unit_of_measure.hh:167:7: required by substitution of 'template<class ... Us> using CommonUnitT = typename au::ComputeCommonUnit::type [with Us = {au::Quarterfeet, au::Trinches}]'
./au/quantity.hh:732:11: required from 'constexpr auto au::detail::convert_and_compare(au::Quantity<U2, R2>, au::Quantity<U2, R2>) [with Op = au::detail::Equal; U1 = au::Quarterfeet; U2 = au::Trinches; R1 = int; R2 = int]'
./au/quantity.hh:743:54: required from 'constexpr bool au::operator==(au::Quantity<U1, R1>, au::Quantity<U2, R2>) [with U1 = au::Quarterfeet; U2 = au::Trinches; R1 = int; R2 = int]'
au/error_examples.cc:121:39: required from here
./au/packs.hh:302:39: error: static assertion failed: Broken strict total ordering: distinct input types compare equal
In file included from ./au/conversion_policy.hh:26,
from ./au/quantity.hh:19,
from ./au/prefix.hh:18,
from ./au/chrono_interop.hh:20,
from ./au/au.hh:17,
from au/error_examples.cc:15:
./au/unit_of_measure.hh: In instantiation of 'struct au::CommonUnit<au::Trinches, au::Quarterfeet>':
./au/packs.hh:220:8: recursively required by substitution of 'template<class Default, template<class ...> class Op, class ... Args> struct au::stdx::experimental::detail::detector<Default, au::stdx::void_t<Op<Args ...> >, Op, Args ...> [with Default = void; Op = au::detail::DimMemberT; Args = {au::CommonUnit<au::Trinches, au::Quarterfeet>}]'
./au/packs.hh:220:8: required from 'struct au::detail::DimImpl<au::CommonUnit<au::Trinches, au::Quarterfeet> >'
./au/unit_of_measure.hh:545:8: required from 'struct au::HasSameDimension<au::CommonUnit<au::Trinches, au::Quarterfeet>, au::Trinches>'
./au/stdx/type_traits.hh:38:59: required from 'struct au::stdx::conjunction<au::HasSameDimension<au::CommonUnit<au::Trinches, au::Quarterfeet>, au::Trinches>, au::detail::HasSameMagnitude<au::CommonUnit<au::Trinches, au::Quarterfeet>, au::Trinches> >'
./au/unit_of_measure.hh:560:8: required from 'struct au::AreUnitsQuantityEquivalent<au::CommonUnit<au::Trinches, au::Quarterfeet>, au::Trinches>'
./au/unit_of_measure.hh:627:8: required from 'struct au::detail::FirstMatchingUnit<au::AreUnitsQuantityEquivalent, au::CommonUnit<au::Trinches, au::Quarterfeet>, au::CommonUnit<au::Trinches, au::Quarterfeet> >'
./au/unit_of_measure.hh:756:8: required from 'struct au::ComputeCommonUnit<au::Quarterfeet, au::Trinches>'
./au/unit_of_measure.hh:167:7: required by substitution of 'template<class ... Us> using CommonUnitT = typename au::ComputeCommonUnit::type [with Us = {au::Quarterfeet, au::Trinches}]'
./au/quantity.hh:732:11: required from 'constexpr auto au::detail::convert_and_compare(au::Quantity<U2, R2>, au::Quantity<U2, R2>) [with Op = au::detail::Equal; U1 = au::Quarterfeet; U2 = au::Trinches; R1 = int; R2 = int]'
./au/quantity.hh:743:54: required from 'constexpr bool au::operator==(au::Quantity<U1, R1>, au::Quantity<U2, R2>) [with U1 = au::Quarterfeet; U2 = au::Trinches; R1 = int; R2 = int]'
au/error_examples.cc:121:39: required from here
./au/unit_of_measure.hh:585:70: error: static assertion failed: Elements must be listed in ascending order
585 | static_assert(AreElementsInOrder<CommonUnit, CommonUnit<Us...>>::value,
| ^~~~~
Compiler error (MSVC 2022 x64)
D:\a\au\au\au.hh(2109): error C2338: static_assert failed: 'Broken strict total ordering: distinct input types compare equal'
D:\a\au\au\au.hh(2109): note: the template instantiation context (the oldest one first) is
error_examples.cc(123): note: see reference to function template instantiation 'bool au::operator ==<au::Quarterfeet,au::Trinches,int,int>(au::Quantity<au::Quarterfeet,int>,au::Quantity<au::Trinches,int>)' being compiled
D:\a\au\au\au.hh(7019): note: see reference to function template instantiation 'auto au::detail::convert_and_compare<au::detail::Equal,au::Quarterfeet,au::Trinches,int,int>(au::Quantity<au::Quarterfeet,int>,au::Quantity<au::Trinches,int>)' being compiled
D:\a\au\au\au.hh(7008): note: see reference to alias template instantiation 'au::CommonUnitT<U1,U2>' being compiled
with
[
U1=au::Quarterfeet,
U2=au::Trinches
]
D:\a\au\au\au.hh(4800): note: see reference to class template instantiation 'au::ComputeCommonUnit<U1,U2>' being compiled
with
[
U1=au::Quarterfeet,
U2=au::Trinches
]
D:\a\au\au\au.hh(5392): note: see reference to class template instantiation 'au::ComputeCommonUnitImpl<U1,U2>' being compiled
with
[
U1=au::Quarterfeet,
U2=au::Trinches
]
D:\a\au\au\au.hh(5380): note: see reference to alias template instantiation 'au::FlatDedupedTypeListT<au::CommonUnit,au::Quarterfeet,au::Trinches>' being compiled
D:\a\au\au\au.hh(1930): note: see reference to class template instantiation 'au::FlatDedupedTypeList<au::CommonUnit,au::CommonUnit<T>,au::CommonUnit<au::Trinches>>' being compiled
with
[
T=au::Quarterfeet
]
D:\a\au\au\au.hh(2251): note: see reference to class template instantiation 'au::InOrderFor<List,T,H>' being compiled
with
[
List=au::CommonUnit,
T=au::Quarterfeet,
H=au::Trinches
]
D:\a\au\au\au.hh(5228): note: see reference to class template instantiation 'au::InOrderFor<au::UnitProduct,A,B>' being compiled
with
[
A=au::Quarterfeet,
B=au::Trinches
]
D:\a\au\au\au.hh(5818): note: see reference to class template instantiation 'au::LexicographicTotalOrdering<A,B,au::detail::OrderByCoarseUnitOrdering,au::detail::OrderByDim,au::detail::OrderByMag,au::detail::OrderByScaleFactor,au::detail::OrderByOrigin,au::detail::OrderAsUnitProduct,au::detail::OrderAsOriginDisplacementUnit,au::detail::OrderByUnitOrderTiebreaker>' being compiled
with
[
A=au::Quarterfeet,
B=au::Trinches
]
D:\a\au\au\au.hh(2125): note: see reference to class template instantiation 'au::LexicographicTotalOrdering<A,B,au::detail::OrderByDim,au::detail::OrderByMag,au::detail::OrderByScaleFactor,au::detail::OrderByOrigin,au::detail::OrderAsUnitProduct,au::detail::OrderAsOriginDisplacementUnit,au::detail::OrderByUnitOrderTiebreaker>' being compiled
with
[
A=au::Quarterfeet,
B=au::Trinches
]
D:\a\au\au\au.hh(2125): note: see reference to class template instantiation 'au::LexicographicTotalOrdering<A,B,au::detail::OrderByMag,au::detail::OrderByScaleFactor,au::detail::OrderByOrigin,au::detail::OrderAsUnitProduct,au::detail::OrderAsOriginDisplacementUnit,au::detail::OrderByUnitOrderTiebreaker>' being compiled
with
[
A=au::Quarterfeet,
B=au::Trinches
]
D:\a\au\au\au.hh(2125): note: see reference to class template instantiation 'au::LexicographicTotalOrdering<A,B,au::detail::OrderByScaleFactor,au::detail::OrderByOrigin,au::detail::OrderAsUnitProduct,au::detail::OrderAsOriginDisplacementUnit,au::detail::OrderByUnitOrderTiebreaker>' being compiled
with
[
A=au::Quarterfeet,
B=au::Trinches
]
D:\a\au\au\au.hh(2125): note: see reference to class template instantiation 'au::LexicographicTotalOrdering<A,B,au::detail::OrderByOrigin,au::detail::OrderAsUnitProduct,au::detail::OrderAsOriginDisplacementUnit,au::detail::OrderByUnitOrderTiebreaker>' being compiled
with
[
A=au::Quarterfeet,
B=au::Trinches
]
D:\a\au\au\au.hh(2125): note: see reference to class template instantiation 'au::LexicographicTotalOrdering<A,B,au::detail::OrderAsUnitProduct,au::detail::OrderAsOriginDisplacementUnit,au::detail::OrderByUnitOrderTiebreaker>' being compiled
with
[
A=au::Quarterfeet,
B=au::Trinches
]
D:\a\au\au\au.hh(2125): note: see reference to class template instantiation 'au::LexicographicTotalOrdering<A,B,au::detail::OrderAsOriginDisplacementUnit,au::detail::OrderByUnitOrderTiebreaker>' being compiled
with
[
A=au::Quarterfeet,
B=au::Trinches
]
D:\a\au\au\au.hh(2125): note: see reference to class template instantiation 'au::LexicographicTotalOrdering<A,B,au::detail::OrderByUnitOrderTiebreaker>' being compiled
with
[
A=au::Quarterfeet,
B=au::Trinches
]
D:\a\au\au\au.hh(2125): note: see reference to class template instantiation 'au::LexicographicTotalOrdering<A,B>' being compiled
with
[
A=au::Quarterfeet,
B=au::Trinches
]
D:\a\au\au\au.hh(5218): error C2338: static_assert failed: 'Elements must be listed in ascending order'
D:\a\au\au\au.hh(5218): note: the template instantiation context (the oldest one first) is
D:\a\au\au\au.hh(5392): note: see reference to class template instantiation 'au::detail::FirstMatchingUnit<au::AreUnitsQuantityEquivalent,T,TargetUnit>' being compiled
with
[
T=au::CommonUnit<au::Trinches,au::Quarterfeet>,
TargetUnit=au::CommonUnit<au::Trinches,au::Quarterfeet>
]
D:\a\au\au\au.hh(5261): note: see reference to class template instantiation 'au::AreUnitsQuantityEquivalent<TargetUnit,H>' being compiled
with
[
TargetUnit=au::CommonUnit<au::Trinches,au::Quarterfeet>,
H=au::Trinches
]
D:\a\au\au\au.hh(5194): note: see reference to class template instantiation 'au::stdx::conjunction<au::HasSameDimension<U1,U2>,au::detail::HasSameMagnitude<U1,U2>>' being compiled
with
[
U1=au::CommonUnit<au::Trinches,au::Quarterfeet>,
U2=au::Trinches
]
D:\a\au\au\au.hh(252): note: see reference to class template instantiation 'au::HasSameDimension<U1,U2>' being compiled
with
[
U1=au::CommonUnit<au::Trinches,au::Quarterfeet>,
U2=au::Trinches
]
D:\a\au\au\au.hh(5179): note: see reference to alias template instantiation 'au::detail::DimT<U1>' being compiled
with
[
U1=au::CommonUnit<au::Trinches,au::Quarterfeet>
]
D:\a\au\au\au.hh(2029): note: see reference to class template instantiation 'au::detail::DimImpl<U1>' being compiled
with
[
U1=au::CommonUnit<au::Trinches,au::Quarterfeet>
]
D:\a\au\au\au.hh(2027): note: see reference to alias template instantiation 'au::detail::DimMemberT<U>' being compiled
with
[
U=au::CommonUnit<au::Trinches,au::Quarterfeet>
]
D:\a\au\au\au.hh(2025): note: see reference to class template instantiation 'au::CommonUnit<T,au::Quarterfeet>' being compiled
with
[
T=au::Trinches
]