static_cast<> 使用返回常量引用的隐式转换运算符的类的行为
static_cast<> behaviour on class with implicit conversion operator that returns a const reference
我有以下类(剥离以仅包含相关部分):
#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++相当陌生。
不,当您调用 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;
};
测试 1
int main() {
Text text(Noisy{});
const auto& s = static_cast<Noisy>(text); // Needs 'const' to bind to temporary.
}
默认构造
移动构造
破坏者
复制构造
破坏者
破坏者
测试 2
int main() {
Text text(Noisy{});
auto& s = static_cast<const Noisy&>(text);
}
默认构造
移动构造
破坏者
破坏者
注意:使用选项 -fno-elide-constructors
进行编译,以避免复制优化。
这里的行为,当你static_cast
std::string
而不是const std::string &
时,是由C++11标准的这一部分决定的:
[expr.static.cast] [5.2.9.4]:
表达式
e
可以使用static_cast<T>(e)
形式的static_cast
显式转换为类型T
如果声明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
copy ctor,并且由于转换运算符未标记为 explicit
,因此以这种方式从Text
获取std::string
是一种有效的隐式转换。
或者这会构造一个我最终得到引用的中间 std::string 吗?
它将构建一个中间std::string
,您最终通过值获得该。
正是出于这个原因,这甚至不是合法的C++。如果你static_cast某件事,你就会得到确切的那件东西,而不是别的。
首先,编译器在强制转换操作的中间创建一个临时变量:
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
如您所见,变量的地址已更改
其次,当您使用auto
时,编译器会隐藏所有const, volatile
等修饰符
void f (int i) {}
auto s = static_cast<const string&>(text);
f(s);
错误:无法将参数的"std::basic_string"转换为"int" "1"到"无效 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
标识符。
- 有没有一种方法可以通过"typedef"为重新定义的基本类型定义特征和强制转换运算符
- 构造函数和转换运算符之间的重载解析
- 分配给转换运算符失败-C++
- 转换运算符不适用于sleep_until
- 继承模板化转换运算符
- 模板转换运算符在 clang 6 和 clang 7 之间的区别
- 如何在模板化转换运算符中消除此构造的歧义?
- 为什么选择转换运算符的重载?
- 如何避免强制转换运算符 () 和访问运算符 [] 冲突?
- 如果可能的话,C++总是更喜欢右值引用转换运算符而不是常量左值引用吗?
- 了解转换运算符的选择C++
- 多个隐式转换运算符
- 这个typedef和转换运算符语法是什么意思
- 为什么转换运算符调用复制构造函数两次,而等效函数只调用它一次
- 类模板忽略了用户定义的转换运算符(非模板不忽略)
- 为什么在std::for_each()返回时调用转换运算符
- 为什么 std::optional 的强制转换运算符被忽略了
- 使用用户定义的转换运算符推导函数模板参数
- 模板转换运算符的分辨率不明确
- 统一初始化是隐式发生的,即使 int 强制转换运算符是使用 explicit 关键字声明的.原因是什么?