3

I'm trying to concatenate string_views in a constexpr. The following is a simplified version of my code:

#include <iostream>
#include <string_view>

using namespace std::string_view_literals;

// concatenate two string_views by copying their bytes
// into a newly created buffer
constexpr const std::string_view operator+
    (const std::string_view& sv1, const std::string_view& sv2)
{
    char buffer[sv1.size()+sv2.size()] = {0};
    for(size_t i = 0; i < sv1.size(); i++)
        buffer[i] = sv1[i];
    for(size_t i = sv1.size(); i < sv1.size()+sv2.size(); i++)
        buffer[i] = sv2[i-sv1.size()];
    return std::string_view(buffer, sv1.size()+sv2.size());
}

int main()
{
    const std::string_view sv1("test1;");
    const std::string_view sv2("test2;");
    std::cout << sv1 << "|" << sv2 << ": " << (sv1+sv2+sv1) << std::endl;
    std::cout << "test1;"sv << "|" << "test2;"sv << ": " <<
        ("test1;"sv+"test2;"sv) << std::endl;
    return 0;
}

However this code does not produce the result I expected. Instead of printing test1;test2;test1 and test1;test2; it prints out correct characters mixed with random characters as if I'm accessing uninitialized memory.

test1;|test2;: F��<��itest;
test1;|test2;: est1;te`�i

However if I remove the constexpr specifier and replace the string_views with strings the above code prints the expected output.

test1;|test2;: test1;test2;test1;
test1;|test2;: test1;test2;

Either I'm missing some obvious mistake in my code or there is something about constexpr that I don't understand (yet). Is it the way I'm creating the buffer for the new string_view? What else could I do? Or is what I'm trying to do impossible? Maybe there is someone who can shed light to this for me.

Scindix
  • 1,254
  • 2
  • 15
  • 32
  • You cannot concatenate *references*. And `string_view` is a reference type. – Nicol Bolas Nov 29 '17 at 16:09
  • 1
    `char buffer[sv1.size()+sv2.size()]` is not standard C++. VLA is an extension. (function arguments are not constexpr). – Jarod42 Nov 29 '17 at 16:18
  • 1
    @Jarod42, actually, `std::string_view::size()` being constexpr means that as long as the function is called with constexpr arguments, this is actually fine. That should be enforced with a local constexpr variable,of course, but I believe his specific example is actually ok. –  Nov 29 '17 at 16:42
  • @Frank: `sv1` is not constexpr (and we don't have way to express that currently). so `sv1.size()` is not `constexpr`. – Jarod42 Nov 29 '17 at 16:45
  • I stand corrected. –  Nov 29 '17 at 16:47
  • @Jarod42 But why does this compile then? I'm using std=c++17 which shouldn't use gnu extensions. – Scindix Nov 29 '17 at 16:50
  • Also the whole function is constexpr. The code shouldn't compile anyway if sv1.size() isn't constexpr, should it? – Scindix Nov 29 '17 at 16:53
  • 1
    You got warning with correct flag: [Demo](http://coliru.stacked-crooked.com/a/678722075ab41345). See also [disable-variable-length-automatic-arrays-in-gcc](https://stackoverflow.com/questions/31710642/disable-variable-length-automatic-arrays-in-gcc) – Jarod42 Nov 29 '17 at 16:58
  • Ah thanks. Didn't know that. – Scindix Nov 29 '17 at 17:01
  • As its name implies `string_view` is not for holding and returning *data* it is for looking at someone else's data. So it has to be looking at data that actually exists. After your function exits the data that the `string_view` is looking at (a local array) gets destroyed. – Galik Nov 29 '17 at 17:12

2 Answers2

3

Your task is fundamentally impossible, since string_view, by definition, needs to have continuous non-owning storage from start to finish. So it'll be impossible to manage the lifetime of the data.

You need to create some kind of concatenated_string<> custom range as your return type if you want to do something like this.

As to the specific reason your code is yielding weird results, it's simply because buffer does not exist anymore when the function exits.

  • Ah it seems possible ... here is the design ' template inline constexpr string operator+(string, string) { return {}; }' just please implement the string and implement transforming from std::string_view into it :) –  Oct 27 '18 at 18:44
2

In return std::string_view(buffer, sv1.size()+sv2.size()); the returned string_view views the buffer which goes out of scope, so you essentially have a dangling reference.

nwp
  • 9,623
  • 5
  • 38
  • 68