C++17 Features: std::apply and fold expressions

1 minute read

Published:

std::apply and fold expressions in C++17: simplifying parameter packs

Often, I find myself in a scenario where I need to “iterate” over the elements inside a tuple. Prior to C++17, the basic idea behind accomplishing this was through the use of parameter packs and std::index_sequence. However, with the introduction of std::apply and fold expressions, this task is much simpler.

#include <functional>
#include <iostream>
#include <typeinfo>
#include <initializer_list>
#include <tuple>
#include <vector>
#include <set>
#include <unordered_set>
#include <string_view>

// stolen from https://stackoverflow.com/questions/81870/is-it-possible-to-print-a-variables-type-in-standard-c/56766138#56766138
// get useful type information without needing to use c++filt
// does the unmangling for you!
template <typename T>
constexpr auto type_name() {
  std::string_view name, prefix, suffix;
#ifdef __clang__
  name = __PRETTY_FUNCTION__;
  prefix = "auto type_name() [T = ";
  suffix = "]";
#elif defined(__GNUC__)
  name = __PRETTY_FUNCTION__;
  prefix = "constexpr auto type_name() [with T = ";
  suffix = "]";
#elif defined(_MSC_VER)
  name = __FUNCSIG__;
  prefix = "auto __cdecl type_name<";
  suffix = ">(void)";
#endif
  name.remove_prefix(prefix.size());
  name.remove_suffix(suffix.size());
  return name;
};

int main(){

  std::initializer_list initializer = {1, 2, 3, 4, 3, 2, 1};
  auto createSet = [=](){
      return std::set<int>(initializer);
  };

  auto createUnorderedSet = [=](){
      return std::unordered_set<int>(initializer);
  };

  auto createVector = [=](){
      return std::vector<int>(initializer);
  };

  auto createMultiSet = [=](){
      return std::multiset<int>(initializer);
  };

  auto printContainer = [](auto& containerCreator){
      auto container = containerCreator();
      std::cout << "Iterating over container of type : " << type_name<decltype(container)>() << "\n";
      for(auto&& entry : container){
          std::cout << "\t" << entry << "\n";
      }
  };

  std::apply([=](auto&&... containerCreator){
      (printContainer(containerCreator),...);
  }, std::tuple{createSet, createUnorderedSet, createVector, createMultiSet});

}

That’s it – just:

std::apply([=](auto&&... containerCreator){
  (printContainer(containerCreator),...);
}, std::tuple{createSet, createUnorderedSet, createVector, createMultiSet});

Check out this example on godbolt: https://godbolt.org/z/dKEr6v43v.