Search code examples
c++c++17variadic-templatesfold-expression

Fold expression with strings and their size


I have C++14 code like this.

I am updating to C++17. Is there a way this to be rewritten as fold expression?

namespace cat_impl_{
    inline size_t catSize(std::string_view const first) {
        return first.size();
    }

    template<typename... Args>
    size_t catSize(std::string_view const first, Args &&... args) {
        return catSize(first) + catSize(std::forward<Args>(args)...);
    }

    inline void cat(std::string &s, std::string_view const first) {
        s.append(first.data(), first.size());
    }

    template<typename... Args>
    void cat(std::string &s, std::string_view const first, Args &&... args) {
        cat(s, first);
        cat(s, std::forward<Args>(args)...);
    }
}

template<typename... Args>
std::string concatenate(Args &&... args){
    // super cheap concatenation,
    // with single allocation

    using namespace cat_impl_;

    size_t const reserve_size = catSize(std::forward<Args>(args)...);

    std::string s;

    s.reserve(reserve_size);

    cat(s, std::forward<Args>(args)...);

    return s;
}

Solution

  • Yes

    template <typename... Args>
    std::size_t catSize (Args &&... args) {
        return (... + std::forward<Args>(args).size());
    }
    

    and

    template <typename... Args>
    void cat (std::string &s, Args ... args) {
        (s.append(args.data(), args.size()), ...);
    }
    

    or also (more generic, not only for std::string_view)

    template <typename ... Args>
    void cat (std::string &s, Args && ... args) {
        (s += std::forward<Args>(args), ...);
    }
    

    Or, maybe, you can avoid at all cat() and catSize() and simply write something as

    template <typename... Args>
    std::string concatenate (Args &&... args) {
    
        std::string s;
    
        s.reserve((args.size() + ...));
    
        return (s += std::forward<Args>(args), ...);
    }
    

    Off Topic: avoid a double std::forward, for the same object, in a function (see double std::forward over your args in your original concatenate()).