Reputation: 5332
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++.
Upvotes: 6
Views: 620
Reputation: 6476
At first, the compiler creates a temp variable in the middle of the cast operation:
int main()
{
string temp = "foo";
cout << &temp << endl;
Text text(move(temp));
auto s = static_cast<string>(text);
cout << &s << endl;
return 0;
}
$ main
0x7fffb9dd7530
0x7fffb9dd7540
As you can see the address of the variable has changed
Secondly, The compiler shadows all the const, volatile
etc. modifiers when you use auto
void f (int i) {}
auto s = static_cast<const string&>(text);
f(s);
error: cannot convert 'std::basic_string' to 'int' for argument '1' to 'void f(int)'
So if you need const std::string&
you need to apply it to the auto
variable:
const auto& s = static_cast<string>(text);
f(s);
error: cannot convert 'const std::basic_string' to 'int' for argument '1' to 'void f(int)'
As you can see the auto
keyword shadows the const
identifier.
Upvotes: 0
Reputation: 15524
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&()
.
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;
};
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
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.
Upvotes: 10
Reputation: 16204
I believe the behavior here, when you static_cast
to std::string
and not const std::string &
, is determined by this part of the C++11 standard:
[expr.static.cast] [5.2.9.4]:
An expression
e
can be explicitly converted to a typeT
using astatic_cast
of the formstatic_cast<T>(e)
if the declarationT t(e)
; is well-formed, for some invented temporary variablet
(8.5). The effect of such an explicit conversion is the same as performing the declaration and initialization and then using the temporary variable as the result of the conversion. The expressione
is used as a glvalue if and only if the initialization uses it as a glvalue.
If you static_cast
to const std::string &
then I would expect that no copy would be made. But if you static_cast
to std::string
, you must get a std::string
value out -- it should always be the case that decltype(static_cast<T>(e))
is the same as type T
. The only way that a new std::string
could be constructed is with the std::string
copy ctor, and because your conversion operator is not marked explicit
, it is a valid implicit conversion to get a std::string
from Text
that way.
Or will this construct an intermediate std::string that I end up getting a reference to?
It will construct an intermediate std::string
that you end up getting by value.
Upvotes: 1
Reputation: 146910
That's not even legal C++ for exactly that reason. If you static_cast to something, you will get out exactly that something and nothing else.
Upvotes: 0