令人惊讶的c风格演员阵容

jcx*_*cxz 4 c++ casting

我正在重构我们的代码库,其中有以下代码(简化):

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();       }
};
Run Code Online (Sandbox Code Playgroud)

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;
}
Run Code Online (Sandbox Code Playgroud)

它打印B(const A&)https://godbolt.org/z/ocWjh1eb3),但为什么呢?我正在转换为const B&而不是转换为B.

Qui*_*mby 7

它调用构造函数,因为这是 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后跟 a const_cast,则无法编译它。

static_cast被选择是因为它考虑了构造函数。请不要使用 c 风格的强制转换,你会发现规则并不那么容易,它迫使你对它们提出问题。

直觉上,我希望 (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;
}

Run Code Online (Sandbox Code Playgroud)

a不是一个B?如果您确实希望它是B,请明确使用reinterpret_cast(但不要)或bit_cast。明智的做法是如果可能的话尝试制作副本。它创建一个新的临时B并将其绑定到const B&. 如果您将其存储到 中const B& b,它将延长临时文件的生命周期,从而使代码安全。