2

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;
}
max66
  • 65,235
  • 10
  • 71
  • 111
Nick
  • 9,962
  • 4
  • 42
  • 80

2 Answers2

4

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()).

max66
  • 65,235
  • 10
  • 71
  • 111
0

This is what I come up with:

#include <string>
#include <string_view>

#include <iostream>

template<typename... Args>
std::string concatenate(Args &&... args){
    static_assert((std::is_constructible_v<std::string_view, Args&&> && ...));

    // super cheap concatenation,
    // with single allocation

    size_t const reserve_size = (std::string_view{ args }.size() + ...);

    std::string s;

    s.reserve(reserve_size);

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

    return s;
}

template<typename... Args>
std::string const &concatenate(std::string &s, Args &&... args){
    static_assert((std::is_constructible_v<std::string_view, Args&&> && ...));

    // super cheap concatenation,
    // sometimes without allocation

    size_t const reserve_size = (std::string_view{ args }.size() + ...);

    s.clear();

    // reserve() will shrink capacity
    if (reserve_size > s.capacity())
        s.reserve(reserve_size);

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

    return s;
}

int main(){
    auto s = concatenate(std::string("Hello"), std::string_view(" "), "world", "!");

    std::cout << s << '\n';
}

I don;t think I need std::forward, since std::string::append supports std::string, const char * and std::string_view anyway.

I still have no idea why fold expressions need to be inside brackets, but seems this is how they need to do.

Nick
  • 9,962
  • 4
  • 42
  • 80
  • A suggestion: call your `concatenate()` with different names (what about you want concatenate a first `std::string`?). And two observation: (a) yes, the syntax require parentheses (brackets?) and (b) I suggest to use `std::forward` with append (`s.append(std::forward(args)...), ...);`) otherwise you can't activate move syntax when available. – max66 Oct 15 '19 at 21:59
  • forgot about passing "things" that convert to std::string – Nick Oct 15 '19 at 22:01
  • >>> what about you want concatenate a first std::string The idea is to use this function in a loop. First time, it will allocate, after that it will act similar to C-style buffer with char array and will not allocate, unless you concatenate with longer string. – Nick Oct 15 '19 at 22:09