[HN Gopher] C++ Templates: How to Iterate through std:tuple: std...
       ___________________________________________________________________
        
       C++ Templates: How to Iterate through std:tuple: std:apply and More
        
       Author : ibobev
       Score  : 39 points
       Date   : 2022-02-14 13:57 UTC (9 hours ago)
        
 (HTM) web link (www.cppstories.com)
 (TXT) w3m dump (www.cppstories.com)
        
       | Koshkin wrote:
       | Using apply, though interesting, looks like a hack TBH. From a
       | practical standpoint, the (explicit) use of index_sequence seems
       | better (and simpler).
        
       | addaon wrote:
       | I feel like this is missing a relatively straightforward step
       | after the the second version. The first version fails to compile
       | with "couldn't infer template argument"; the second version
       | corrects this at the cost of naming explicit types. The author
       | then goes off in a few directions to avoid naming the types...
       | but he has them right there, why not just use them?
       | -     std::apply(printImpl<int, int, double>, tp);       +
       | std::apply(printImpl<Args...>, tp);
        
         | [deleted]
        
         | MauranKilom wrote:
         | 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
         | 
         | And the function that the author arrives at similarly supports
         | other types than std::tuple:
         | 
         | https://godbolt.org/z/hzqbMWrjq
        
           | joebaf wrote:
           | Hi, 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
        
           | addaon wrote:
           | That'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 wrote:
             | Keep 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 wrote:
             | Well, 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
             | 
             | 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 wrote:
               | That 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.
        
       ___________________________________________________________________
       (page generated 2022-02-14 23:01 UTC)