Info
-
Did you know about C++23 proposal
Structured Bindings can introduce a Pack
?
Example
#include <tuple>
#include <cassert>
int main() {
auto [first, ...ts] = std::tuple{1, 2 ,3};
assert(1 == first);
}
Puzzle
- Can you implement a function which returns first and last elements from a variadic pack?
[[nodiscard]] constexpr auto first_and_last(auto... args); // TODO
int main() {
using namespace boost::ut;
"first and last"_test = [] {
expect(std::tuple{1, 2} == first_and_last(1, 2));
expect(std::tuple{1, 3} == first_and_last(1, 2, 3));
expect(std::tuple{0, 2} == first_and_last(0, 10, 2));
};
}
Solutions
[[nodiscard]] constexpr auto first_and_last(auto... args)
{
auto [first, ...ts] = std::tuple{args...};
auto tail = std::tuple{ts...};
return std::tuple{first, std::get<sizeof...(ts) - 1>(tail)};
}
[[nodiscard]] constexpr auto first_and_last(auto... args) {
auto const [first, ...tail] = std::tuple{args...};
auto const [...mid, last] = std::tuple{args...}; // Note, using `tail` here causes ICE.
return std::tuple{first, last};
}
[[nodiscard]] constexpr auto first_and_last(auto... args)
requires(sizeof...(args) == 2)
{
// This function is only needed because an empty pack seems to ICE in every
// way I could think of writing it. Remove this overload for a demonstration
// of the ICE.
return std::tuple{args...};
}
[[nodiscard]] constexpr auto first_and_last(auto... args) {
auto [first, ... _, last] = std::tuple{args...};
return std::tuple{first, last};
}
[[nodiscard]] constexpr auto first_and_last(auto... args); // TODO
int main() {
using namespace boost::ut;
"first and last"_test = [] {
expect(std::tuple{1, 2} == first_and_last(1, 2));
expect(std::tuple{1, 3} == first_and_last(1, 2, 3));
expect(std::tuple{0, 2} == first_and_last(0, 10, 2));
};
}
[[nodiscard]] constexpr auto first_and_last(auto... args) requires (sizeof...(args) >= 2) {
auto [first, ...suffix] = std::tuple{args...};
auto [...prefix, last] = std::tuple{args...};
return std::tuple{first, last};
}
[[nodiscard]] constexpr auto first_and_last(auto first, auto last) {
return std::tuple{first, last};
}
[[nodiscard]] constexpr auto first_and_last(auto... args) requires (sizeof...(args) > 2) {
auto [first, ...ts, last] = std::tuple{args...};
return std::tuple{first, last};
}
[[nodiscard]] constexpr auto first_and_last(auto arg1, auto arg2, auto... args){
return std::make_tuple(arg1, [](auto recur, auto ... args){
return recur(recur, args ...);
}([](auto recur, auto arg, auto ... args){
if constexpr(sizeof ... (args) == 0){
return arg;
} else {
return recur(recur, args ...);
}
}, arg2, args ...));
}
[[nodiscard]] constexpr auto first_and_last(auto... args) {
return std::make_tuple(std::get<0>(std::make_tuple(args...)), std::get<sizeof...(args)-1>(std::make_tuple(args...)) );
}
[[nodiscard]] constexpr auto first_and_last(auto... args) requires (sizeof...(args) == 2){
auto [first, last] = std::tuple{args...};
return std::tuple{first, last};
}
[[nodiscard]] constexpr auto first_and_last(auto... args) requires (sizeof...(args) > 2){
auto [first, ...mid, last] = std::tuple{args...};
return std::tuple{first, last};
}
constexpr auto last(auto... args) {
const auto [...rest, _last] = std::tuple{args...};
return _last;
}
constexpr auto first(auto... args) {
const auto [_first, ...rest] = std::tuple{args...};
return _first;
}
[[nodiscard]] constexpr auto first_and_last(auto... args){
return std::tuple{first(args...), last(args...)};
}
[[nodiscard]] constexpr auto first_and_last(auto first, auto... args) {
return std::tuple{first, (args, ...)};
}
[[nodiscard]] constexpr auto first_and_last(auto... args) {
if constexpr (sizeof...(args) != 2) {
auto [first, ... values, last] = std::tuple{args...};
return std::tuple{first, last};
} else {
return std::tuple{args...};
}
}
[[nodiscard]] constexpr auto first_and_last(auto... args){
std::common_type_t<decltype(args)...> first;
(void)(((first = args, true) || ...));
std::common_type_t<decltype(args)...> last;
(void)(((last = args, false) || ...));
return std::make_tuple(first,last);
}
[[nodiscard]] constexpr auto first_and_last(auto... args){
auto [first, ...ts, last] = std::tuple<decltype(args)...>(args...);
return std::make_tuple(first,last);
}
[[nodiscard]] constexpr auto first_and_last(auto first, auto... rest) {
return std::tuple{first, (..., (rest = rest))};
}