[英]Surprising c-style cast
我正在重构我们的代码库,其中我有以下代码(简化):
template <typename T>
class TVector3;
template <typename T>
class TVector4;
template <typename T>
struct TVector4
{
TVector3<T>& V3() { return (TVector3<T> &) *this; }
const TVector3<T>& V3() const { return (const TVector3<T> &) *this; }
};
template <typename T>
struct TVector3
{
template <typename U>
constexpr TVector3(const TVector4<U>& v) noexcept { }
};
typedef TVector3<float> Vec3f;
typedef TVector4<float> Vec4f;
struct RGBA
{
Vec4f rgba;
operator Vec3f() const { return rgba.V3(); }
};
clang 警告我returning reference to local temporary object
( https://godbolt.org/z/ccxbjv771 )。 显然(const TVector3<T> &) *this
导致调用TVector3(const TVector4<U>& )
,但为什么呢?
直觉上,我会期望(const TVector3<T> &)
表现得像重新解释演员,或者至少演员看起来像(const TVector3<T>) *this
(没有&
)它会有点意义我认为编译器选择了该构造函数调用进行转换。
另一个更简单的例子:
#include <iostream>
struct A { };
struct B
{
B(const A& ) { std::cout << "B(const A&)" << std::endl; }
};
int main()
{
A a;
(const B&) a;
return 0;
}
它打印B(const A&)
( https://godbolt.org/z/ocWjh1eb3 ),但为什么呢? 我正在转换为const B&
而不是B
。
它调用构造函数,因为这是 c 样式转换的规则:
cppreference:当遇到 C 风格的强制转换表达式时,编译器尝试将其解释为以下强制转换表达式,按以下顺序:
- a)
const_cast<new_type>(expression);
- b)
static_cast<new_type>(expression)
,带有扩展: 派生类的指针或引用额外允许转换为指向明确基类的指针或引用(反之亦然),即使基类不可访问(即,此强制转换忽略私有继承说明符)。 同样适用于将指向成员的指针转换为指向明确非虚基的成员的指针;- c)
static_cast
(带扩展)后跟const_cast
;- d)
reinterpret_cast<new_type>(expression);
- e)
reinterpret_cast
后跟const_cast
。 选择满足相应转换运算符要求的第一个选项,即使它无法编译(参见示例)。 如果static_cast
const_cast
可以以多种方式解释为static_cast
后跟const_cast
,则无法编译它。
选择static_cast
是因为它考虑了构造函数。 请不要使用 c-style cast,你会发现规则并不那么容易,它迫使你对它们提出问题。
直觉上,我会期望 (const TVector3 &) 表现得像重新解释演员
你不会想要那样的,它会破坏严格的别名。 但是如果你删除构造函数,它会很高兴地按照 d) 那样做。
#include <iostream>
struct A { };
struct B
{
B(const A& ) { std::cout << "B(const A&)" << std::endl; }
};
struct C
{
};
int main()
{
A a;
const B& temp1 = (B&) a;// Ctor, copy, prolonged-life good.
const B& temp2 = (const B&) a;// Ctor, copy, prolonged-life good.
B& dangling_temp = (B&) a;// Ctor, copy, no prolongment->dangling ref, BAD.
(const C&) a;// REINTERPET_CAST
//(const C) a;// Compiler error, good.
(const C*) &a;// REINTERPET_CAST
return 0;
}
但是a
不是B
吗? 如果您真的真的希望它是B
,请明确使用reinterpret_cast
(但不要)或bit_cast
。 明智的做法是尽可能尝试制作副本。 它创建一个新的临时B
并将其绑定到const B&
。 如果你将它存储到const B& b
,它会延长临时的生命周期,使代码安全。
它打印
B(const A&)
,但为什么呢? 我正在转换为const B&
而不是B
。
的类型的a
是A
,它不能被绑定到const B&
直接。 它需要首先通过B::B(const A& )
转换为B
; 然后转换后的临时B
绑定到const B&
。 (对 const 的左值引用可以绑定到临时变量。)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.