182 void complain(
const char *file,
int line,
const char *func,
185 void complain(
const char *file,
int line,
const char *func,
192#define DUNE_SIMD_CHECK(expr) \
193 ((expr) ? void() : complain(__FILE__, __LINE__, __func__, #expr))
197#define DUNE_SIMD_CHECK_OP(expr) \
198 ((expr) ? void() : complain(__FILE__, __LINE__, __func__, \
199 DUNE_SIMD_OPNAME, #expr))
205 return std::forward<T>(t);
210 static bool is42(
const V &v)
216 good &= (
lane(l, v) == Scalar<V>(42));
231 lane(l, vec) = l + 1;
237 static bool is123(
const V &v)
243 good &= (
lane(l, v) == Scalar<V>(l+1));
249 static V leftVector()
255 lane(l, res) = Scalar<V>(l+1);
260 static V rightVector()
268 lane(l, res) = Scalar<V>((l)%7+1);
273 static T leftScalar()
279 static T rightScalar()
286 template<
class Dst,
class Src>
287 using CopyRefQual = Impl::CopyRefQual<Dst, Src>;
294 template<
class Op,
class... Vectors>
296 decltype(std::declval<Op>().
312 "must not be references, and must not include "
314 [[maybe_unused]] T a{};
318 [[deprecated(
"Warning: please include bool in the Rebinds for "
319 "simd type V, as Masks are not checked otherwise.")]]
324 template<
class V,
class Rebinds,
template<
class>
class RebindPrune,
325 template<
class>
class RebindAccept,
class Recurse>
326 void checkRebindOf(Recurse recurse)
329 using T =
typename decltype(target)::type;
332 using W = Rebind<T, V>;
333 log_ <<
"Type " << className<V>() <<
" rebound to "
334 << className<T>() <<
" is " << className<W>() <<
std::endl;
337 "types must not be references, and must not include "
339 static_assert(lanes<V>() == lanes<W>(),
"Rebound types must have "
340 "the same number of lanes as the original vector "
343 "must have the bound-to scalar type");
345 if constexpr (RebindPrune<W>{}) {
346 log_ <<
"Pruning check of Simd type " << className<W>()
350 using Impl::debugTypes;
351 static_assert(debugTypes<T, V, W>(RebindAccept<W>{}),
352 "Rebind<T, V> is W, but that is not accepted "
354 recurse(MetaType<W>{});
359 "rebound to its own scalar type must be the same type "
360 "as the original type");
362 "rebound to bool must be the mask type for that type");
364 constexpr bool hasBool = Impl::TypeInList<bool, Rebinds>::value;
378 "return type of lanes<V>() should be std::size_t");
380 "return type of lanes(V{}) should be std::size_t");
383 [[maybe_unused]]
constexpr auto size = lanes<V>();
389 void checkDefaultConstruct()
391 { [[maybe_unused]] V vec; }
392 { [[maybe_unused]] V vec{}; }
393 { [[maybe_unused]] V vec = {}; }
404 lane(l, vec) = l + 1;
407 using MLRes =
decltype(
lane(0, vec));
410 "Result of lane() on a mutable lvalue vector must "
411 "either be a mutable reference to a scalar of that "
412 "vector or a proxy object (which itself may not be a "
413 "reference nor const).");
419 using CLRes =
decltype(
lane(0, vec2));
422 "Result of lane() on a const lvalue vector must "
423 "either be a const lvalue reference to a scalar of that "
424 "vector or a proxy object (which itself may not be a "
425 "reference nor const).");
427 "Result of lane() on a const lvalue vector must not be "
428 "assignable from a scalar.");
433 using RRes =
decltype(
lane(0, prvalue(vec)));
444 "Result of lane() on a rvalue vector V must be "
445 "Scalar<V> or Scalar<V>&&.");
456 void checkCopyMoveConstruct()
465 { V
ref(make123<V>()); V vec (ref);
467 { V
ref(make123<V>()); V vec =
ref ;
469 { V
ref(make123<V>()); V vec {
ref};
471 { V
ref(make123<V>()); V vec = {
ref};
473 {
const V
ref(make123<V>()); V vec (ref);
475 {
const V
ref(make123<V>()); V vec =
ref ;
477 {
const V
ref(make123<V>()); V vec {
ref};
479 {
const V
ref(make123<V>()); V vec = {
ref};
483 { V
ref(make123<V>()); V vec (std::move(ref));
485 { V
ref(make123<V>()); V vec = std::move(ref) ;
487 { V
ref(make123<V>()); V vec {std::move(ref)};
489 { V
ref(make123<V>()); V vec = {std::move(ref)};
494 void checkBroadcastVectorConstruct()
497 { Scalar<V>
ref = 42; V vec (ref);
499 { Scalar<V>
ref = 42; V vec =
ref ;
505 {
const Scalar<V>
ref = 42; V vec (ref);
507 {
const Scalar<V>
ref = 42; V vec =
ref ;
515 { Scalar<V>
ref = 42; V vec (std::move(ref));
517 { Scalar<V>
ref = 42; V vec = std::move(ref) ;
526 void checkBroadcastMaskConstruct()
529 { Scalar<V>
ref = 42; V vec (ref);
533 { Scalar<V>
ref = 42; V vec {
ref};
537 {
const Scalar<V>
ref = 42; V vec (ref);
541 {
const Scalar<V>
ref = 42; V vec {
ref};
547 { Scalar<V>
ref = 42; V vec (std::move(ref));
551 { Scalar<V>
ref = 42; V vec {std::move(ref)};
558 template<
class FromV,
class ToV>
562 FromV fromVec = make123<FromV>();
563 auto toVec = implCast<ToV>(fromVec);
564 static_assert(
std::is_same<
decltype(toVec), ToV>::value,
565 "Unexpected result type for implCast<ToV>(FromV&)");
571 const FromV fromVec = make123<FromV>();
572 auto toVec = implCast<ToV>(fromVec);
573 static_assert(
std::is_same<
decltype(toVec), ToV>::value,
574 "Unexpected result type for implCast<ToV>(const "
580 auto toVec = implCast<ToV>(make123<FromV>());
581 static_assert(
std::is_same<
decltype(toVec), ToV>::value,
582 "Unexpected result type for implCast<ToV>(FromV&&)");
594 checkImplCast<V, V>();
595 checkImplCast<V, LoopV>();
596 checkImplCast<LoopV, V>();
601 void checkBroadcast()
606 auto vec = broadcast<V>(ref);
608 "Unexpected result type for broadcast<V>()");
614 const Scalar<V>
ref = 42;
615 auto vec = broadcast<V>(ref);
617 "Unexpected result type for broadcast<V>()");
622 auto vec = broadcast<V>(Scalar<V>(42));
624 "Unexpected result type for broadcast<V>()");
629 auto vec = broadcast<V>(42);
631 "Unexpected result type for broadcast<V>()");
636 auto vec = broadcast<V>(42.0);
638 "Unexpected result type for broadcast<V>()");
644 void checkBracedAssign()
647 { V
ref = make123<V>(); V vec; vec = {
ref};
649 {
const V
ref = make123<V>(); V vec; vec = {
ref};
657 void checkBracedBroadcastAssign()
676#define DUNE_SIMD_POSTFIX_OP(NAME, SYMBOL) \
677 struct OpPostfix##NAME \
680 auto operator()(V&& v) const \
681 -> decltype(std::forward<V>(v) SYMBOL) \
683 return std::forward<V>(v) SYMBOL; \
687#define DUNE_SIMD_PREFIX_OP(NAME, SYMBOL) \
688 struct OpPrefix##NAME \
691 auto operator()(V&& v) const \
692 -> decltype(SYMBOL std::forward<V>(v)) \
694 return SYMBOL std::forward<V>(v); \
712#pragma GCC diagnostic push
713#pragma GCC diagnostic ignored "-Wpragmas"
714#pragma GCC diagnostic ignored "-Wunknown-warning-option"
715#pragma GCC diagnostic ignored "-Wbool-operation"
717#pragma GCC diagnostic pop
719#undef DUNE_SIMD_POSTFIX_OP
720#undef DUNE_SIMD_PREFIX_OP
722 template<
class V,
class Op>
724 IsCallable<Op(decltype(lane(0, std::declval<V>())))>::value>
727#define DUNE_SIMD_OPNAME (className<Op(V)>())
729 auto val = leftVector<std::decay_t<V>>();
733 auto &&result = op(
static_cast<V
>(arg));
752 ==
static_cast<T
>(op(
lane(l,
static_cast<V
>(val)))));
756 for(
std::size_t l = 0; l < lanes<std::decay_t<V> >(); ++l)
758#undef DUNE_SIMD_OPNAME
761 template<
class V,
class Op>
763 not IsCallable<Op(decltype(lane(0, std::declval<V>())))>::value>
771 template<
class V,
class Op>
772 void checkUnaryOpsV(Op op)
774 checkUnaryOpV<V&>(op);
775 checkUnaryOpV<const V&>(op);
776 checkUnaryOpV<V&&>(op);
790#define DUNE_SIMD_INFIX_OP(NAME, SYMBOL) \
791 struct OpInfix##NAME \
793 template<class V1, class V2> \
794 decltype(auto) operator()(V1&& v1, V2&& v2) const \
796 return std::forward<V1>(v1) SYMBOL std::forward<V2>(v2); \
798 template<class S1, class S2> \
799 auto scalar(S1&& s1, S2&& s2) const \
800 -> decltype(std::forward<S1>(s1) SYMBOL std::forward<S2>(s2)); \
811#define DUNE_SIMD_ASSIGN_OP(NAME, SYMBOL) \
812 struct OpInfix##NAME \
814 template<class V1, class V2> \
815 decltype(auto) operator()(V1&& v1, V2&& v2) const \
817 return std::forward<V1>(v1) SYMBOL std::forward<V2>(v2); \
819 template<class S1, class S2> \
820 auto scalar(S1& s1, S2&& s2) const \
821 -> decltype(s1 SYMBOL std::forward<S2>(s2)); \
824#define DUNE_SIMD_REPL_OP(NAME, REPLFN, SYMBOL) \
825 struct OpInfix##NAME \
827 template<class V1, class V2> \
828 decltype(auto) operator()(V1&& v1, V2&& v2) const \
830 return Simd::REPLFN(std::forward<V1>(v1), std::forward<V2>(v2)); \
832 template<class S1, class S2> \
833 auto scalar(S1&& s1, S2&& s2) const \
834 -> decltype(std::forward<S1>(s1) SYMBOL std::forward<S2>(s2)); \
878 static constexpr bool isComparisonOp =
886#undef DUNE_SIMD_INFIX_OP
887#undef DUNE_SIMD_REPL_OP
888#undef DUNE_SIMD_ASSIGN_OP
891 struct OpInfixComma {};
893 template<
class T1,
class T2>
897#define DUNE_SIMD_OPNAME (className<OpInfixComma(T1, T2)>())
899 std::declval<T2>())), T2>::value,
900 "Type and value category of the comma operator must "
901 "match that of the second operand");
910#pragma GCC diagnostic push
911#pragma GCC diagnostic ignored "-Wunused-value"
912 auto &&result = (
static_cast<T1
>(arg1),
913 static_cast<T2
>(arg2));
914#pragma GCC diagnostic pop
934#undef DUNE_SIMD_OPNAME
959 template<
class V1,
class V2,
class Op>
961 checkBinaryOpVV(MetaType<V1>, MetaType<V2>, Op op)
963#define DUNE_SIMD_OPNAME (className<Op(V1, V2)>())
965 "Internal testsystem error: called with two types that "
966 "don't decay to the same thing");
969 auto vref1 = leftVector<std::decay_t<V1>>();
970 auto vref2 = rightVector<std::decay_t<V2>>();
977 auto &&vopres = op(
static_cast<V1
>(vop1),
static_cast<V2
>(vop2));
978 using VR =
decltype(vopres);
982 "The result must have the same number of lanes as the "
986 static_assert(!isComparisonOp<Op> ||
988 Mask<std::decay_t<V1> > >::value,
989 "The result of a comparison must be of type Mask<V>.");
993 using T = Scalar<std::decay_t<VR> >;
1000 ==
static_cast<T
>(op(
lane(l,
static_cast<V1
>(vref1)),
1001 lane(l,
static_cast<V2
>(vref2)))));
1012#undef DUNE_SIMD_OPNAME
1015 template<
class V1,
class V2,
class Op>
1017 checkBinaryOpVV(MetaType<V1>, MetaType<V2>, Op op)
1025 template<
class V1,
class V2>
1026 void checkBinaryOpVV(MetaType<V1>, MetaType<V2>, OpInfixComma)
1029 "Internal testsystem error: called with two types that "
1030 "don't decay to the same thing");
1069 template<
class V1,
class T2,
class Op>
1071 checkBinaryOpVS(MetaType<V1>, MetaType<T2>, Op op)
1073#define DUNE_SIMD_OPNAME (className<Op(V1, T2)>())
1076 "Internal testsystem error: called with a scalar that "
1077 "does not match the vector type.");
1080 auto sinit2 = rightScalar<std::decay_t<T2>>();
1083 auto vref1 = leftVector<std::decay_t<V1>>();
1084 auto sref2 = sinit2;
1091 auto &&vopres = op(
static_cast<V1
>(vop1),
static_cast<T2
>(sop2));
1092 using VR =
decltype(vopres);
1096 "The result must have the same number of lanes as the "
1111 ==
static_cast<T
>(op(
lane(l,
static_cast<V1
>(vref1)),
1112 static_cast<T2
>(sref2) )));
1121#undef DUNE_SIMD_OPNAME
1124 template<
class V1,
class T2,
class Op>
1126 checkBinaryOpVS(MetaType<V1>, MetaType<T2>, Op op)
1134 template<
class V1,
class T2>
1135 void checkBinaryOpVS(MetaType<V1>, MetaType<T2>, OpInfixComma)
1139 "Internal testsystem error: called with a scalar that "
1140 "does not match the vector type.");
1172 template<
class V1,
class T2,
class Op>
1174 checkBinaryOpVVAgainstVS(MetaType<V1>, MetaType<T2>, Op op)
1176#define DUNE_SIMD_OPNAME (className<Op(V1, T2)>())
1179 "Internal testsystem error: called with a scalar that "
1180 "does not match the vector type.");
1183 auto sinit2 = rightScalar<std::decay_t<T2>>();
1186 auto vop1 = leftVector<std::decay_t<V1>>();
1187 using V2 = CopyRefQual<V1, T2>;
1191 op(
static_cast<V1
>(vop1),
static_cast<V2
>(vop2));
1197#undef DUNE_SIMD_OPNAME
1200 template<
class V1,
class T2,
class Op>
1202 checkBinaryOpVVAgainstVS(MetaType<V1>, MetaType<T2>, Op op)
1210 template<
class V1,
class T2>
1211 void checkBinaryOpVVAgainstVS(MetaType<V1>, MetaType<T2>, OpInfixComma)
1248 template<
class V1,
class V2,
class Op>
1250 checkBinaryOpVP(MetaType<V1>, MetaType<V2>, Op op)
1252 using P2 =
decltype(
lane(0, std::declval<V2>()));
1253 using T2 = CopyRefQual<Scalar<V2>, V2>;
1254#define DUNE_SIMD_OPNAME (className<Op(V1, P2)>())
1256 "Internal testsystem error: called with two vector "
1257 "types whose scalar types don't match.");
1260 auto sinit2 = rightScalar<Scalar<V2>>();
1263 auto vref1 = leftVector<std::decay_t<V1>>();
1264 auto sref2 = sinit2;
1269 lane(0, vop2) = sref2;
1273 op(
static_cast<V1
>(vop1),
lane(0,
static_cast<V2
>(vop2)));
1274 using VR =
decltype(vopres);
1278 "The result must have the same number of lanes as the "
1285 using T =
Scalar<
decltype(vopres)>;
1293 ==
static_cast<T
>(op(
lane(l,
static_cast<V1
>(vref1)),
1294 static_cast<T2
>(sref2) )));
1303#undef DUNE_SIMD_OPNAME
1306 template<
class V1,
class V2,
class Op>
1308 checkBinaryOpVP(MetaType<V1>, MetaType<V2>, Op op)
1316 template<
class V1,
class V2>
1317 void checkBinaryOpVP(MetaType<V1>, MetaType<V2>, OpInfixComma)
1328 struct OpInfixSwappedArgs
1332 template<
class V1,
class V2>
1333 decltype(
auto)
operator()(V1&& v1, V2&& v2)
const
1335 return orig(std::forward<V2>(v2), std::forward<V1>(v1));
1337 template<
class S1,
class S2>
1338 auto scalar(S1&& s1, S2&& s2)
const
1339 ->
decltype(orig.scalar(std::forward<S2>(s2), std::forward<S1>(s1)));
1342 template<
class T1,
class V2,
class Op>
1343 void checkBinaryOpSV(MetaType<T1> t1, MetaType<V2> v2, Op op)
1345 checkBinaryOpVS(v2, t1, OpInfixSwappedArgs<Op>{op});
1348 template<
class T1,
class V2>
1349 void checkBinaryOpSV(MetaType<T1>, MetaType<V2>, OpInfixComma)
1352 Scalar<std::decay_t<V2> > >::value,
1353 "Internal testsystem error: called with a scalar that "
1354 "does not match the vector type.");
1360 template<
class V1,
class V2,
class Op>
1361 void checkBinaryOpPV(MetaType<V1> v1, MetaType<V2> v2, Op op)
1363 checkBinaryOpVP(v2, v1, OpInfixSwappedArgs<Op>{op});
1366 template<
class V1,
class V2>
1367 void checkBinaryOpPV(MetaType<V1>, MetaType<V2>, OpInfixComma)
1402 template<
class Op,
class W1,
class T2>
1404 decltype(std::declval<Op>().scalar(std::declval<W1>(),
1405 std::declval<T2>()));
1407 template<
class W1,
class V2,
class Op>
1408 void checkBinaryOpMixedVP(MetaType<W1>, MetaType<V2>, Op op)
1412 using T2 =
const Scalar<V>&;
1416 if constexpr (Std::is_detected_v<ScalarResult, Op, W1, V2>)
1417 if constexpr (Std::is_detected_v<MixedResult, Op, W1, T2>)
1419#define DUNE_SIMD_OPNAME \
1420 (className<Op(W1, decltype(lane(0, std::declval<V2>())))>())
1422 const auto sinit2 = rightScalar<Scalar<V> >();
1425 auto wref1 = leftVector<W>();
1426 auto sref2 = sinit2;
1430 auto vop2 = V(Scalar<V>(0));
1431 lane(0, vop2) = sinit2;
1434 auto &&wrefres = op(
static_cast<W1
>(wref1),
static_cast<T2
>(sref2));
1436 op(
static_cast<W1
>(wop1),
lane(0,
static_cast<V2
>(vop2)));
1441 "An operation with a proxy must have the same result "
1442 "type as the operation with the scalar it "
1455#undef DUNE_SIMD_OPNAME
1459 template<
class W1,
class V2>
1460 void checkBinaryOpMixedVP(MetaType<W1>, MetaType<V2>, OpInfixComma)
1465 template<
class V1,
class W2,
class Op>
1466 void checkBinaryOpMixedPV(MetaType<V1> v1, MetaType<W2> w2, Op op)
1468 checkBinaryOpMixedVP(w2, v1, OpInfixSwappedArgs<Op>{op});
1471 template<
class V1,
class W2>
1472 void checkBinaryOpMixedPV(MetaType<V1>, MetaType<W2>, OpInfixComma)
1479 using MixedPrecisionScalars = TypeList<float, double, int>;
1483 template<
class V,
class F>
1484 static void forEachMixedPrecisionPartner(F f)
1488 using W =
Rebind<
typename decltype(s)::type, V>;
1490 !Impl::IsAnyLoop<W>::value)
1521 template<
class T1,
class V2,
class Op>
1522 void checkBinaryOpVVAgainstSV(MetaType<T1> t1, MetaType<V2> v2, Op op)
1524 checkBinaryOpVVAgainstVS(v2, t1, OpInfixSwappedArgs<Op>{op});
1527 template<
class V1,
class T2>
1528 void checkBinaryOpVVAgainstSV(MetaType<V1>, MetaType<T2>, OpInfixComma)
1536 template<
class T1,
class T2,
bool condition,
class Checker>
1537 void checkBinaryRefQual(Checker checker)
1539 if constexpr (condition) {
1548 template<
class V,
class Checker>
1555 constexpr bool do_ =
false;
1556 constexpr bool do_SV =
true;
1557 constexpr bool do_VV =
true;
1558 constexpr bool do_VS =
true;
1565 constexpr bool do_VVShift =
1566 requires(
const Scalar<V>& s,
const V& v) {
1571#define DUNE_SIMD_DO(M1, M2, M3, V1, V2, V3, NAME) \
1572 checker(bool_constant<isMask ? do_##M1 : do_##V1>{}, \
1573 bool_constant<isMask ? do_##M2 : do_##V2>{}, \
1574 bool_constant<isMask ? do_##M3 : do_##V3>{}, \
1627 void checkAutoCopy()
1629 using RValueResult =
decltype(
autoCopy(
lane(0, std::declval<V>())));
1631 "Result of autoCopy() must always be Scalar<V>");
1633 using MutableLValueResult =
1636 "Result of autoCopy() must always be Scalar<V>");
1638 using ConstLValueResult =
1641 "Result of autoCopy() must always be Scalar<V>");
1643 V vec = make123<V>();
1650 void checkBoolReductions()
1692 auto mixedVec = broadcast<M>(0);
1694 lane(l, mixedVec) = (l % 2);
1698 (
allTrue (
static_cast<M&
>(mixedVec)) ==
false);
1700 (
anyTrue (
static_cast<M&
>(mixedVec)) == (lanes<M>() > 1));
1702 (
allFalse(
static_cast<M&
>(mixedVec)) == (lanes<M>() == 1));
1704 (
anyFalse(
static_cast<M&
>(mixedVec)) ==
true);
1708 (
allTrue (
static_cast<const M&
>(mixedVec)) ==
false);
1710 (
anyTrue (
static_cast<const M&
>(mixedVec)) == (lanes<M>() > 1));
1712 (
allFalse(
static_cast<const M&
>(mixedVec)) == (lanes<M>() == 1));
1714 (
anyFalse(
static_cast<const M&
>(mixedVec)) ==
true);
1730 std::declval<V>())), V>::value,
1731 "The result of cond(M, V, V) should have exactly the type V");
1735 std::declval<const V&>(),
1736 std::declval<const V&>())), V>::value,
1737 "The result of cond(const M&, const V&, const V&) should have "
1738 "exactly the type V");
1742 std::declval<V&>())), V>::value,
1743 "The result of cond(M&, V&, V&) should have exactly the type V");
1745 V vec1 = leftVector<V>();
1746 V vec2 = rightVector<V>();
1751 auto mixedResult = broadcast<V>(0);
1752 auto mixedMask = broadcast<M>(
false);
1755 lane(l, mixedMask ) = (l % 2);
1756 lane(l, mixedResult) =
lane(l, (l % 2) ? vec1 : vec2);
1763 void checkBoolCond()
1767 std::declval<V>())), V>::value,
1768 "The result of cond(bool, V, V) should have exactly the type V");
1772 std::declval<const V&>(),
1773 std::declval<const V&>())), V>::value,
1774 "The result of cond(const bool&, const V&, const V&) should have "
1775 "exactly the type V");
1780 std::declval<V&>())), V>::value,
1781 "The result of cond(bool&, V&, V&) should have exactly the type V");
1783 V vec1 = leftVector<V>();
1784 V vec2 = rightVector<V>();
1792 checkHorizontalMinMax() {}
1796 checkHorizontalMinMax()
1800 "The result of max(V) should be exactly Scalar<V>");
1804 "The result of min(V) should be exactly Scalar<V>");
1808 "The result of max(V) should be exactly Scalar<V>");
1812 "The result of min(V) should be exactly Scalar<V>");
1814 const V vec1 = leftVector<V>();
1822 checkBinaryMinMax() {}
1833 std::declval<V>())), V>::value,
1834 "The result of Simd::max(V, V) should be exactly V");
1837 std::declval<V>())), V>::value,
1838 "The result of Simd::min(V, V) should be exactly V");
1842 std::declval<V&>())), V>::value,
1843 "The result of Simd::max(V&, V&) should be exactly V");
1846 std::declval<V&>())), V>::value,
1847 "The result of Simd::min(V&, V&) should be exactly V");
1849 const V arg1 = leftVector<V>();
1850 const V arg2 = rightVector<V>();
1852 V maxExp(Scalar<V>(0)), minExp(Scalar<V>(0));
1874 const V vec1 = leftVector<V>();
1878 const char *sep =
"";
1882 stream <<
lane(l, vec1);
1885 reference += stream.
str();
1893 if(
lanes(vec1) == 1)
1901 stream <<
vio(vec1);
1906#undef DUNE_SIMD_CHECK
2004 template<
class V,
class Rebinds,
2005 template<
class>
class RebindPrune =
IsLoop,
2009 if(seen_.
emplace(typeid (V)).second ==
false)
2017 auto recurse = [
this](
auto w) {
2018 using W =
typename decltype(w)::type;
2019 this->
template check<W, Rebinds, RebindPrune, RebindAccept>();
2021 checkRebindOf<V, Rebinds, RebindPrune, RebindAccept>(recurse);