繁体   English   中英

令人惊讶的 c 风格演员表

[英]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 objecthttps://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

的类型的aA ,它不能被绑定到const B&直接。 它需要首先通过B::B(const A& )转换为B 然后转换后的临时B绑定到const B& (对 const 的左值引用可以绑定到临时变量。)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM