c++stlc++14libc++

Is this behaviour of std::quote bug?


I want to do the same thing as std::quote with a custom type, but I thinking about miss used of this kind of API with a temporary rvalue. After some dinging with std::quoted, I discovered the following problem:

To be efficient std::quoted force to store a const reference or a pointer to avoid a deep copy of the source object, but there are no mechanism to avoid to store the quoted result. If we store it, then delete the source object, and finally stream the stored result we try to access to the deleted reference or pointer.

The example below try to illustrate the problem:

#include <string>
#include <iostream>
#include <iomanip>

class String
{
public:
    explicit String(const std::string & s) : _s(s) {std::cout << "String\n";}
    ~String() { std::cout << "~String\n"; _s = "ERROR TRY ACCESS DELETED STRING";}
    const std::string & getS() const {return _s;}
private:
    std::string _s;
};

int main()
{
    std::cout << std::quoted(String("test").getS()) << '\n';
    std::cout << '\n';

    auto q = std::quoted(String("test").getS());
    std::cout << q << '\n';
    return 0;
}

This example print:

String
"test"
~String

String
~String
"p DELETED STRING"

We can see the same problem with gcc(trunk) and clang(trunk).


Solution

  • This is to be expected. Or rather: this is not to be expected to work.

    From cppreference:

    Allows insertion and extraction of quoted strings, such as the ones found in CSV or XML.

    When used in an expression out << quoted(s, delim, escape), where out is an output stream with char_type equal to CharT and, for overloads 2-4, traits_type equal to Traits, behaves as a FormattedOutputFunction, which inserts into out a sequence of characters seq constructed as follows:

    a) First, the character delim is added to the sequence

    b) Then every character from s, except if the next character to output equals delim or equals escape (as determined by the stream's traits_type::eq), then first appends an extra copy of escape

    c) In the end, delim is appended to seq once more

    And

    Return value

    Returns an object of unspecified type such that the described behavior takes place.

    And the "described bahvior" is the above: "When used in an expression out << quoted(s, delim, escape), where out [...]" then the string s is outputted to the stream. When that string s does no longer exist you cannot output it to a stream.


    Actually what is written on cppreferece is what you can also find in the stadnard, just a little reworded, unfortunately without adding lots of clarify. From the standard [quoted.manip#2]:

    Returns: An object of unspecified type such that if out is an instance of basic_­ostream with member type char_­type the same as charT and with member type traits_­type, which in the second and third forms is the same as traits, then the expression out << quoted(s, delim, escape) behaves as a formatted output function of out. This forms a character sequence seq, initially consisting of the following elements: [...]

    Note that it only states what happens in an expression out << quoted(s, delim, escape). And thats the only usage you can rely on. Maybe the note helps to clarify:

    [Note 1: Quoted manipulators provide string insertion and extraction of quoted strings (for example, XML and CSV formats). Quoted manipulators are useful in ensuring that the content of a string with embedded spaces remains unchanged if inserted and then extracted via stream I/O. — end note]

    std::quoted is a io-manipulator. Its return value is not meant to be used for anything but to be passed to a streams operator<< or operator>>.