我有以下类(剥离以仅包含相关部分):
#include <string>
class Text
{
private:
std::string _text;
public:
Text(std::string&& text) :
_text(std::move(text))
{
}
operator const std::string&() const
{
return _text;
}
};
我的问题是:如果我想获得const std::string&
,我可以这样做而不会受到任何惩罚:
Text text("fred");
auto& s = static_cast<std::string>(text);
或者这会构建一个中间std::string
,我最终会得到一个引用?这种情况是否有标准方法?我是C ++的新手。
答案 0 :(得分:10)
不,当您调用static_cast<std::string>(text)
时,您正在调用隐式定义的复制构造函数并创建临时对象。
但是,如果你要打电话
auto& s = static_cast<const std::string&>(text);
,然后您将正确地调用显式转换运算符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.
}
默认构造
移动构造
析构函数
复制构造
析构函数
析
int main() {
Text text(Noisy{});
auto& s = static_cast<const Noisy&>(text);
}
默认构造
移动构造
析构函数
析
注意:使用选项-fno-elide-constructors
进行编译以避免复制省略优化。
答案 1 :(得分:1)
我认为,static_cast
到std::string
而不是const std::string &
时,此处的行为取决于C ++ 11标准的这一部分:
[expr.static.cast] [5.2.9.4]:
表达式
e
可以使用T
形式的static_cast
显式转换为static_cast<T>(e)
类型 如果声明T t(e)
;对于一些发明的临时变量t
(8.5),它是格式良好的。这样的效果 显式转换与执行声明和初始化然后使用临时转换相同 变量作为转换的结果。当且仅当初始化时,表达式e
用作glvalue 将它用作glvalue。
如果您static_cast
到const std::string &
,那么我希望不会复制。但是,如果您static_cast
到std::string
,则必须获得std::string
值 - decltype(static_cast<T>(e))
与T
类型相同。构建新std::string
的唯一方法是使用std::string
副本ctor,并且因为您的转换运算符未标记为explicit
,所以它是一个有效的隐式转换来获取std::string
1}}来自Text
那样。
或者这会构造一个中间的std :: string,我最终会得到一个引用?
它将构建一个中间std::string
,您最终会按获取。
答案 2 :(得分:0)
出于这个原因,这甚至都不是合法的C ++。如果你对某些东西进行static_cast,那么你就会明白那个东西而不是其他东西。
答案 3 :(得分:0)
首先,编译器在强制转换操作的中间创建一个临时变量:
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
如您所见,变量的地址已更改
其次,当您使用const, volatile
时,编译器会隐藏所有auto
等修饰符
void f (int i) {}
auto s = static_cast<const string&>(text);
f(s);
错误:无法将'std :: basic_string'转换为'int'作为参数 '1'到'void f(int)'
因此,如果您需要const std::string&
,则需要将其应用于auto
变量:
const auto& s = static_cast<string>(text);
f(s);
错误:无法将'const std :: basic_string'转换为'int' 参数'1'到'void f(int)'
正如您所见,auto
关键字隐藏了const
标识符。