Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

static_cast<> behaviour on class with implicit conversion operator that returns a const reference

Tags:

c++

c++11

I have the following class (stripped down to contain only the relevant parts):

#include <string>

class Text
{
private:
    std::string _text;

public:
    Text(std::string&& text) :
        _text(std::move(text))
    {
    }

    operator const std::string&() const
    {
        return _text;
    }
};

My question is: if I want to obtain a const std::string&, can I do it like this without any penalty:

 Text text("fred");

 auto& s = static_cast<std::string>(text);

Or will this construct an intermediate std::string that I end up getting a reference to? Is there a standard approach to this kind of scenario? I am reasonably new to C++.

like image 799
keith Avatar asked Jan 16 '16 16:01

keith


People also ask

What does static_cast mean in C++?

The static_cast operator converts variable j to type float . This allows the compiler to generate a division with an answer of type float . All static_cast operators resolve at compile time and do not remove any const or volatile modifiers.

What happens when static_cast fails?

As we learnt in the generic types example, static_cast<> will fail if you try to cast an object to another unrelated class, while reinterpret_cast<> will always succeed by "cheating" the compiler to believe that the object is really that unrelated class.

Is static_cast an operator?

The static_cast operator converts a given expression to a specified type.

What does static_cast void do?

If, in your code, you know you do not need a result somewhere, you can use the static_cast<void> method to mark the result as discarded – but the compiler will consider the variable used then and no longer create a warning or error.


1 Answers

No, when you're calling static_cast<std::string>(text), you're calling the implicitly defined copy constructor and creating a temporary object.

However, if you would be calling

auto& s = static_cast<const std::string&>(text);

,then you would correctly be calling the explicit conversion operator operator const Noisy&().

Let's try it out

struct Noisy {
    Noisy() { std::cout << "Default construct" << std::endl; }
    Noisy(const Noisy&) { std::cout << "Copy construct" << std::endl; }
    Noisy(Noisy&&) { std::cout << "Move construct" << std::endl; }
    Noisy& operator=(const Noisy&) { std::cout << "C-assign" << std::endl; return *this; }
    Noisy& operator=(Noisy&&) { std::cout << "M-assign" << std::endl; return *this; }
    ~Noisy() { std::cout << "Destructor" << std::endl; }
};

class Text {
public:
    Text(Noisy&& text) : _text(std::move(text)) {}
    operator const Noisy&() const { return _text; }
private:
    Noisy _text;
};

Test 1

int main() {
    Text text(Noisy{});
    const auto& s = static_cast<Noisy>(text); // Needs 'const' to bind to temporary.
}

Default construct
Move construct
Destructor
Copy construct
Destructor
Destructor

Test 2

int main() {
    Text text(Noisy{});
    auto& s = static_cast<const Noisy&>(text);
}

Default construct
Move construct
Destructor
Destructor

Note: Compiled using option -fno-elide-constructors to avoid copy elision optimizations.

like image 123
Felix Glas Avatar answered Nov 14 '22 22:11

Felix Glas