Prints "B"
Prints "C"
Prints "C"
Prints "B"
Doesn't compile!
Prints "C"
Doesn't compile!!!
Prints "C"
Prints "C"
OddDetector with a single template parameter and a member function print() that prints "Odd" if the class's template parameter is itself a template with an odd number of template arguments and "Nope" otherwise (don't worry about types with non-type template parameters)OddDetector with a single template parameter [span.strike and a member function print()] that prints "Odd" if the class's template function's parameter is an instance of a type that is itself a template with an odd number of template arguments and "Nope" otherwise (don't worry about types with non-type template parameters)OddDetector that constraints types to be templates with an odd number of template parametersOddDetector that constraints types to be templates with an odd number of template parametersOddDetectorImpl function inline!vector but so generic that all you know about the value type is that it's addable?type_list with std::sorttype_list with std::sortParameter packs are the compile-time data structure of choice. As of C++11, C++ has language support for lists of types. We would be foolish to work with anything else.
Eric Niebler, ericniebler.com/2014/11/13/tiny-metaprogramming-library
That's it!
std::type_identitystd::type_identity was added in C++20std::type_identitystd::type_identityis a metaprogramming idiom that's essentially equivalent to:
X with arguments Y and Z is "invoked" by getting the type member like this:type_list sort is:Key is a function object that takes a std::type_identity-wrapped type and returns something that works with operator<()Key functionKey argument to our sort metafunction is a function object that takes a std::type_identity-wrapped type and returns something that works with operator<()apply member class template that has a type and/or a value memberapply is a "metafunction"Key functionstd::type_identity-wrapped argument will be the new C++20 convention for metafunction predicatesstd::type_identity?T is void?int[4]?int[]?type_list?sort so that it takes a pack directly?zip)?Ts...)std::sort to sort those indices based on the value of the Key function for the type at each indextype_liststd::integer_sequence!index_sequenceindex_sequence_forconstexpr variable with some logic that spans multiple statements, we can use the immediately invoked lambda idiom to initialize it:std::sort!std::array because of class template argument deduction (CTAD).std::ranges::sort!at metafunctionat for each sorted index..."...sorted_indices:constexpr variable, we can just use it directly in a template argumentTs... = int, double, char) and (Idxs... = 0, 1, 2), then:becomes
Idxs is still an unexpanded pack, so we get
Compare function for the sortcompare argument to std::sort is a function object that takes two elements of the range and returns true if the first one is ordered before the second one (and false otherwise)Compare function for the sortconstexpr on a function like std::sort means "maybe this might be evaluated at compile-time"constexpr in my C++North 2022 talk (slides: dsh.fyi/cppnorth-2022), video: dsh.fyi/cppnorth-2022-video)Compare function for the sortvariant is another way we often do this kind of thing:Key with std::variantvariant of all of the types we might want to pass to the Key function looks like thisstd::type_identity<at_t<idx1, Ts...>>{}?std::visit on the idx1 element of this array to get the result of applying the key function!Compare function for the sortcompare function for std::sort isset() function passes the value to impl() and ignored the value that it expects impl() to throwimpl() stores its argument in a static variable during the initializer of another static variable _ and throws before that initializer can returnimpl() always runs stored = v; in a thread-safe way every time it is called, since the initializer of _ never completes without an exception.impl().static variables are not part of lambda capture. The reference to stored is part of the type of the lambda on line 5 (rather than any particular instance).get() effectively executes the body of the lambda in line 5 without ever executing line 4 👻impl() for each template parameter—and thus a unique instance of stored and a unique lambda type for the returnset() and get() refer to the version of impl() associated with a given template parameteroperator[] with a Tuple42, 0x5A6DF3, 123ull, -123'456'789ll42.0, 42., 4.2e1, 0xa.bp10f, 0X0p-1"hello", L"ABC", R"foo(hello world)foo"'a', U'🍌', '\n'true, falsenullptr25u or 17U is a unsigned int (larger values will have a larger type)25l or 17L is a long int (or larger for larger values)25uz or 17UZ is a std::size_t25z or 17Z is a std::make_signed_t<std::size_t>index class template to hold the indices as non-type template parameters:std::integral_constant though)std::get()constexpr contextsconstexpr contextsstatic_assert in a constexpr function for an expression that's dependent on function parameters ("constexpr" means "maybe this might be evaluated in a constant expression"). But you can just use assert as usualconstexpr mean?constexpr mean?constexpr you mean!constexpr variablesconstexpr functionsconstexpr if statements (a.k.a., if constexpr)constexpr uses require different things to be constant expressionsconstexpr variables require constant initializersconstexpr if statements require constant conditional expressionsconstexpr functions require ???constexpr mean (for a function)?Look at how we can use it...
constexpr variable...static_assert...constexpr functions can be used with runtime parameters!constexpr mean? (And why?)constexpr means maybe this might be evaluated in a constant expression"constexpr function that never can be used in a constant expression is ill-formed:assert in a constexpr function?constexpr function from the earlier slide?assert macro is usually written something like this:assert(expr()) is only a constant expression if expr() evaluates to trueconstexpr really mean?"constexpr functions and function templates in C++ generally speaking mean maybe constexpr. Not all instantiations or evaluations must be invocable at compile time, it's just that there must be at least one set of function arguments in at least one instantiation that works."
...but this might change soon!