Scindix
Scindix

Reputation: 1304

concatenate string_views in constexpr

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.

Upvotes: 6

Views: 12647

Answers (3)

欢乐的Xiaox
欢乐的Xiaox

Reputation: 562

To concat string_view, create storage for new string_view.

To concat string_view and results in constexpr, create static storage for new string_view.

In C++20, constexpr staitc storage can be created easily.

Before C++20, you have to use template specialization with Parameter pack to assign each element, which is possible but very complicated.

There is a example in C++20:

template<const std::string_view &sv1, const std::string_view &sv2>
struct StringViewCat{
    static constexpr auto get_arr()
    {
        std::array<char, sv1.size() + sv2.size()> arr;
        for(auto i = 0; i < sv1.size(); ++i)
            arr[i] = sv1[i];
        for(auto i = 0; i < sv2.size(); ++i)
            arr[i+sv1.size()] = sv2[i];
        
        return arr;
    }
    constexpr static std::array<char, sv1.size() + sv2.size()> arr = get_arr();
    constexpr static std::string_view data{arr.data(), arr.size()};
};

Usage:

    static constexpr auto a = "a"sv;
    static constexpr auto b = "b"sv;
    constexpr auto c = StringViewCat<a, b>::data;

Upvotes: 2

nwp
nwp

Reputation: 10011

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.

Upvotes: 2

user4442671
user4442671

Reputation:

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.

Upvotes: 2

Related Questions