4 ms·
I believe the author goes this way because the goal is a generic function that can iterate over tuple-like types. If you know that you'll work with std::tuple s
by MauranKilom 5y ago
I believe the author goes this way because the goal is a generic function that can iterate over tuple-like types. If you know that you'll work with std::tuple specifically, then you can indeed just deduce the contained types and call it day. But std::apply is more generic than that. For example, you can std::apply a std::pair:
https://godbolt.org/z/vfcd5YoWE https://godbolt.org/z/vfcd5YoWE
And the function that the author arrives at similarly supports other types than std::tuple:
https://godbolt.org/z/hzqbMWrjq https://godbolt.org/z/hzqbMWrjq
- addaon 5y agoThat's fair. Although it's straightforward to adopt the first solution for arbitrary tuple-like types as well: template <template <typename...> typename Wrapper, typename... Args> void printTupleApplyFn(const Wrapper<Args...>& tp) { // no further changes
- dataflow 5y agoKeep in mind that in general that approach only works if you assume the template parameters are exactly the tuple types. Any extra parameters (or different representation) would make it not work. In general you don't want to use template template parameters because of this. It's analogous to depending on the physical layout of a struct rather than the names of its members.
- MauranKilom 5y agoWell, the authoritative answer to "which types are contained in this tuple-like type" is std::tuple_element(_t). That's exactly what the std::apply machinery relies on, which is why it's pretty convenient to build things around that. Your suggestion fails for e.g. std::array, which is also tuple-like: https://godbolt.org/z/qYhdfjrY3 https://godbolt.org/z/qYhdfjrY3 User-defined types can also specialize the required templates to opt-in to tuple-like behavior. In fact, doing so enables structured binding support, so it's not unlikely to see something like Vec3 getCoords(); auto [x, y, z] = getCoords(); (where Vec3 specializes std::tuple_element etc.), and you wouldn't want your tuple printing code to error out when given a Vec3 either. In fact, Vec3 might not even be a template in the first place!
- addaon 5y agoThat all makes sense, I agree totally. One of the challenges with this sort of unmotivated code (unmotivated in a literal sense, not a derogatory sense -- we don't know what's motivating it) is that it's very hard to know how general a solution to design. I love the C++ template universe when things "just work," but it's very easy to get into a cycle where just a /bit/ more complexity gets you a /bit/ more generality -- only to realize that after beautifully polishing that modular code, you only use it in one or two contexts. std::apply is a good example of putting that generality and complexity into the standard library; if a caller can then use that directly, even with loss of generality, that's usually my preference. If nothing else, it means folks encountering the caller will have fewer layers to understand, and many fewer non-standard layers.
- joebaf 5y agoHi, Author here - I went to lambdas because I got some compilation issues... because some stupid mistakes the code with std::apply(printImpl<Args...>, tp) didn't compile... But now I see the issue and it's improved. So the version: template <typename... Args> void printTupleApplyFn(const std::tuple<Args...>& tp) { std::cout << "("; std::apply(printImpl<Args...>, tp); // << std::cout << ")"; } is fine and probably the easiest