2

There is already a question for this here: How to repeat a string a variable number of times in C++? However because the question was poorly formulated primarily answers about character multiplication were given. There are two correct, but expensive answers, so I'll be sharpening the requirement here.


Perl provides the x operator: http://perldoc.perl.org/perlop.html#Multiplicative-Operators which would let me do this:

$foo = "0, " x $bar;

I understand that I can do this with the helper functions such as those in the other answer. I want to know can I do this without my own helper function? My preference would be something that I could initialize a const string with, but if I can't do that I'm pretty sure that this could be answered with a standard algorithm and a lambda.

Community
  • 1
  • 1
Jonathan Mee
  • 37,899
  • 23
  • 129
  • 288
  • The short answer is: nope. – Sam Varshavchik Feb 19 '16 at 13:44
  • 4
    "standard algorithm and a lambda" - how is that not a helper function? please clarify. what are you trying to achieve here? – Karoly Horvath Feb 19 '16 at 13:52
  • 1
    @KarolyHorvath I think these parts go together "do this without my own helper function" - "if I can't do that I'm pretty sure that this could be answered with a standard algorithm and a lambda" – James Adkison Feb 19 '16 at 13:53
  • @JamesAdkison: Which makes the whole question vague. – Karoly Horvath Feb 19 '16 at 13:57
  • So you want to know how to do this without a helper function but if you can't then you want to know how to do this in a "standard" way? – NathanOliver Feb 19 '16 at 13:58
  • Do you want to avoid creating a temporary string for the initialization of the const string? – Karoly Horvath Feb 19 '16 at 13:59
  • 1
    Maybe op wants a custom multiplication operator for `std::string`? – Simon Kraemer Feb 19 '16 at 13:59
  • There's not a whole lot of difference between functions and lambdas. Why exactly lambdas are OK but functions are not? – n. m. could be an AI Feb 19 '16 at 13:59
  • @n.m. You're correct. This is really a code beautification thing, I wouldn't normally write a function for something that is only used in one location, so I'm trying to avoid it here as well. – Jonathan Mee Feb 19 '16 at 14:03
  • @SimonKraemer Sounds a very useful proposition. However my preference here is something simple, cause I'm only using it in one place, I'd like everything to fit on one line ideally. – Jonathan Mee Feb 19 '16 at 14:05
  • @NathanOliver That's correct, though it seems that I had trouble communicating that. I wish I could do this in C++: `const auto foo = "0, "s x bar;` But since I can't, and presuming there isn't a `string` constructor for this I want to do it in a standard function. – Jonathan Mee Feb 19 '16 at 14:08
  • @KarolyHorvath This is a code beautification question primarily. I don't want to write a function that I only use in one place. And while what I really want to do is, `const auto foo = "0, "s x bar;` I'd settle for a non-`const` `foo` that is initialized in a standard function with a lambda. – Jonathan Mee Feb 19 '16 at 14:12
  • @JonathanMee: use any existing code from the question you've linked. – Karoly Horvath Feb 19 '16 at 14:21
  • @KarolyHorvath What are you suggesting here, none of the answers to the question I link accomplish this without writing my own function? – Jonathan Mee Feb 19 '16 at 14:23
  • @KarolyHorvath They all do... what? Require me to write my own function? Perhaps you intended as is mentioned in [Simon Kraemer's answer](http://stackoverflow.com/a/35507367/2642059) that I just use these functions as lambdas? – Jonathan Mee Feb 19 '16 at 14:56
  • *sigh* They all work fine without writing your own function. I'm off. – Karoly Horvath Feb 19 '16 at 15:11
  • @KarolyHorvath Got it, you're saying I could just write the functions directly into my code, which wouldn't really get this done in a single line or beautify the code, so it wouldn't make a good answer, but I suppose I could see how looking at that as a valid answer could make the question unclear. – Jonathan Mee Feb 19 '16 at 15:28
  • single line? irrelevant. beautiful? definitely. it's clean code. get your priorities right. – Karoly Horvath Feb 19 '16 at 15:30
  • @KarolyHorvath While I appreciate and understand your opinion. Code beauty and minimal length are irrelevant but if we can speak in terms of simplicity, readability, and maintainability, I think a better answer could be reached. I realize those are all subjective, but I do think I've got a good answer I'll post shortly... – Jonathan Mee Feb 19 '16 at 15:38
  • well, @SimonKraemer 's answers are really ugly. They are needlessly complicated. – Karoly Horvath Feb 19 '16 at 15:40
  • @KarolyHorvath Though it wasn't exactly what I wanted, I did appreciate his multiplication operator, the rest of the code... could be improved. Though I have to say, it hardly seems fair for you to criticize his answers till you've written a better way to solve this. – Jonathan Mee Feb 19 '16 at 15:43
  • 1
    @KarolyHorvath Most of the linked answers are about repeating a single character. Only [**one**](http://stackoverflow.com/a/34321702/4181011) is about repeating a complete string. I don't take any offense that my answers are *"really ugly"* and *"needlessly complicated"* but would you mind telling me what exactly bothers you about them? The linked answer is far more complicated. – Simon Kraemer Feb 19 '16 at 15:49
  • 2
    @KarolyHorvath I also agree that it is unecessary to use a lambda function when it is only used in one place. It would be far easier to just write the code in place. – Simon Kraemer Feb 19 '16 at 15:50
  • @SimonKraemer: introducing an operator that's going to be used in one place in the whole codebase? tell me how's that sound. – Karoly Horvath Feb 19 '16 at 15:50
  • 1
    @KarolyHorvath Depends on the use case. If I can foresee that it will be used in multiple places in the future I think it's legit. Also if it improves readability. Whether to write it as operator function, lambda function or whatelse is just some kind of preference. – Simon Kraemer Feb 19 '16 at 15:52

2 Answers2

3

You can either override the multiplication operator

#include <string>
#include <sstream>
#include <iostream>


std::string operator*(const std::string& str, size_t times)
{
    std::stringstream stream;
    for (size_t i = 0; i < times; i++) stream << str;
    return stream.str();
}

int main() {
    std::string s = "Hello World!";
    size_t times = 5;

    std::string repeated = s * times;
    std::cout << repeated << std::endl;

    return 0;
}

... or use a lambda ...

#include <string>
#include <sstream>
#include <iostream>

int main() {
    std::string s = "Hello World!";
    size_t times = 5;

    std::string repeated = [](const std::string& str, size_t times) {std::stringstream stream; for (size_t i = 0; i < times; i++) stream << str; return stream.str(); } (s, times);
    std::cout << repeated << std::endl;

    return 0;
}

... or use a lambda with reference capturing ...

#include <string>
#include <sstream>
#include <iostream>

int main() {
    std::string s = "Hello World!";
    size_t times = 5;

    std::string repeated = [&s, &times]() {std::stringstream stream; for (size_t i = 0; i < times; i++) stream << str; return stream.str(); }();
    std::cout << repeated << std::endl;

    return 0;
}

Instead of using std::stringstream you could also use std::string in combination with std::string::reserve(size_t) as you already know (or can calculate) the size of the result string.

std::string repeated; repeated.reserve(str.size() * times);
for (size_t i = 0; i < times; i++) repeated.append(str);
return repeated;

This might be faster: Compare http://goo.gl/92hH9M with http://goo.gl/zkgK4T

Simon Kraemer
  • 5,700
  • 1
  • 19
  • 49
0

It is possible to do this using just a standard algorithm and a lambda with generate_n, but it still cannot initialize a const string it needs to be done in a separate line:

string foo;
const auto bar = 13U;

generate_n(back_inserter(foo), bar * 3U, [](){
    static const char multiplicand[] = "0, ";
    static const auto length = strlen(multiplicand);
    static auto i = 0U;
    return multiplicand[i++ % length];});

I've created a live example here: http://ideone.com/uIt2Ee But as is probably been made plain by all the question comments, the requirement of doing this in a single line results in inferior code. Right off the bat, we can see that the bare constant, 3, represents the size of multiplicand and unnecessarily requires changes to the initialization of multiplicand to also update this literal.

The obvious improvement that should be made is:

string foo;
const auto bar = 13U;
const char multiplicand[] = "0, ";
const auto length = strlen(multiplicand);

generate_n(back_inserter(foo), bar * length, [&](){
    static auto i = 0U;
    return multiplicand[i++ % length];
});

The next improvement would be eliminating the reallocation as foo grows, which could be expensive if bar or length is large. That can be accomplished by constructing foo with sufficient space to contain the entire generated string:

const auto bar = 13U;
const char multiplicand[] = "0, ";
const auto length = strlen(multiplicand);
string foo(bar * length, '\0');

generate_n(foo.begin(), bar * length, [&](){
    static auto i = 0U;
    return multiplicand[i++ % length];
});

[Live Example]

Jonathan Mee
  • 37,899
  • 23
  • 129
  • 288
  • `string::reserve` instead of the resize at construction would avoid to initialize `foo` twice. – Jarod42 Feb 23 '16 at 16:39
  • @Jarod42 Ummm... I don't call either `reserve` or `resize`. Are you talking about my construction argument that `calloc`s `foo`: `string foo(bar * length, '\0');`? – Jonathan Mee Feb 23 '16 at 16:46
  • Yes, I talked about `string foo(bar * length, '\0')`. To be replaced by `string foo; foo.reserve(bar * length);`. – Jarod42 Feb 23 '16 at 17:05
  • @Jarod42 I'm not sure if I follow why it wouldn't be preferable to `calloc` this up front. Wouldn't that cause `foo` to be `malloc`ed and then `realloc`ed, since a `string` must always contain at least 1 `char` (the terminating NULL.) – Jonathan Mee Feb 23 '16 at 18:03
  • There is still small string optimization, so I expect only one allocation for `reserve`. – Jarod42 Feb 23 '16 at 18:19
  • @Jarod42 Hmmm, fair point. So you're saying that since we can't guarantee that the compiler will `calloc`, it may `malloc` and assign each element, in which case it'll be doing all those assignments twice, but only once if we just use reserve. I think I agree with that. I'll edit when I wrap up the linked question: http://stackoverflow.com/q/35580152/2642059 – Jonathan Mee Feb 23 '16 at 18:36