-2

Please note that's just a curious question, I don't need a problem solution!

I have a method that takes in a string reference (string &some_string) and only reads from referenced string. Writing some code I forgot that it needs a reference and passed a quoted string (not a variable, just like "something") and IDE suggested casting it to string reference, as it won't compile. I didn't give it a thought and applied the suggestion (now passing (string &) "something"). And my program crashed as it reached this piece of code. So, why exactly would it cause a crash, compiling without even a warning (g++, c++11)? I don't really understand it since I'm only reading from this string.

Example:

#include <string>
#include <iostream>


using namespace std;

class CharSet
{
private:
    const string basis = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    const int N = 26;
    string array;  // string containing all element of the set
    int length;


    bool add_element(char chr) // Add an element (as char) if not already. Returns true if added, false if not.
    {
        bool b = false;
        if (!in_set(chr) && basis.find(chr) != string::npos) {
            array += chr;
            length++;
            b = true;
        }
        return b;
    }

    bool in_set(char chr) const  // Checks whether an element (as char) is in set. Returns corresponding bool value.
    {
        bool b = false;
        if (array.find(chr) != string::npos) b = true;
        return b;
    }
public:
    explicit CharSet(string& str)  // str - string of possible elements
    {
        array = "";
        length = 0;
        int len = (int) str.length();
        for (int i = 0; i < len; i++)
            add_element(str[i]);
        if (str.length() != length)
            cout << "\nSome mistakes corrected. Elements read: " << array << endl;
    }
};

int main()
{
    CharSet A((string &)"AKEPTYUMRX");
    getchar();
    return 0;
}
GaussGun
  • 100
  • 1
  • 9
  • 9
    `"a string literal"` is not a `std::string`, but a `const char[N]`. Treating objects as something they are not is something that is likely to cause a crash. – NathanOliver Oct 26 '21 at 18:41
  • @NathanOliver casting doesn't solve this problem? – GaussGun Oct 26 '21 at 18:43
  • 3
    Nope, it is the cause of the problem. – NathanOliver Oct 26 '21 at 18:44
  • 1
    @NathanOliver Casting a string literal to `std::string` should work due to the corresponding non-explicit converting constructor, right? Does casting to `std::string&` cause the problem by itself? – Daniel Langr Oct 26 '21 at 18:45
  • @NathanOliver I still surprised this completely tricked both compiler and IDE (CLion's got pretty smart assistance) while it's not something like messing with dynamic memory – GaussGun Oct 26 '21 at 18:46
  • Casting it to `std::string` creates a temporary. [Temporaries cannot bind to non-const lvalue references](https://stackoverflow.com/questions/1565600/how-come-a-non-const-reference-cannot-bind-to-a-temporary-object). If your method doesn't attempt to mutate `some_string`, you can make it take a `const string &` instead of a `string &`. If it _does_, are you sure you should be calling it on a temporary? – Nathan Pierson Oct 26 '21 at 18:46
  • 2
    @DanielLangr Casting it to a `std::string` would be fine, it will create a new string object initialized with the string literal contents. Casting to a `string&` just treats the `const char[N]` as is it was a `std::string`, which will cause all sorts of issues. – NathanOliver Oct 26 '21 at 18:46
  • 2
    I'd really like to see a [mcve]. – Fred Larson Oct 26 '21 at 18:47
  • 1
    And an indication why c-style casts are dangerous. – Captain Giraffe Oct 26 '21 at 18:47
  • 7
    @GaussGun Doing `(string &) "something"` basically tells the compiler: Look, I know what I'm doing so just do that cast and let me get on with the code. Had you tried the C++ way and used `static_cast("string literal")`, you would have received a nice compiler error. – NathanOliver Oct 26 '21 at 18:48
  • @NathanOliver I guess that answers my question completely. Why don't you make it into an answer? – GaussGun Oct 26 '21 at 18:50
  • @FredLarson if you still need it, here it is. Could accidentally remove something while simplifying but should work. – GaussGun Oct 26 '21 at 18:59
  • I'm with @CaptainGiraffe This is why C-sty;le casts are a bad idea in C++ code. – Tim Randall Oct 26 '21 at 19:40

1 Answers1

1

A cast like this

(string)"a string literal"

constructs a new std::string by passing "a string literal" as an argument to its constructor. It is equivalent to static_cast<std::string>("a string literal"). It is completely valid, even though C-style casts in C++ are a bad idea in general.

On the other hand, a cast like this

(string&)"a string literal"

constructs a reference to an std::string object that resides at the same location as the string literal. Since there is no such object at that location, using this reference results in undefined behaviour (often expressed as a crash). This cast is equivalent to reinterpret_cast<std::string&>("a string literal"). You might know that reinterpret_cast is dangerous and should be avoided as much as possible. With reinterpret_cast, the compiler trusts the programmer nearly totally, which is not really a wise thing to do, but such is the C++ way.

For your function to be able to accept a string literal, it should have a const std::string& parameter, or perhaps better, if you are using C++17, an std::string_view.

n. m. could be an AI
  • 112,515
  • 14
  • 128
  • 243
  • Cases when it behaves as intended (if you pass a short enough string) are just a part of undefined behaviour, right? – GaussGun Oct 31 '21 at 10:22
  • I guess they might be, although given common implementations of std::string, I don't really see how it could happen. But in theory everything is possible. – n. m. could be an AI Oct 31 '21 at 17:09