比起转换构造函数,更喜欢转换运算符

prefer conversion operator over conversion constructor

本文关键字:转换 更喜欢 运算符 构造函数      更新时间:2023-10-16

我有以下代码片段:

class A
{
public:
  A() : x_(0), y_(0) {}
  A(int x, int y) : x_(x), y_(y) {}
  template<class T>
  A(const T &rhs) : x_(rhs.x_), y_(rhs.y_)
  {
  }
  int x_, y_;
};
class B
{
public:
  B() {}
  operator A() const { return A(c[0],c[1]); }
  int c[2];
};
void f()
{
  B b;
  (A)b; // << here the error appears, compiler tries to use 
        //         template<class T> A(const T &rhs)
}

编译器为什么使用A的构造函数?如何使用BA的转换运算符?

我使用MSVS2010编译器。它给了我这些错误:

main.cpp(9): error C2039: 'x_' : is not a member of 'B'
          main.cpp(17) : see declaration of 'B'
          main.cpp(28) : see reference to function template instantiation 'A::A<B>(const T &)' being compiled
          with
          [
              T=B
          ]
main.cpp(9): error C2039: 'y_' : is not a member of 'B'
          main.cpp(17) : see declaration of 'B'

UPD:好吧,正如纳瓦兹所说的隐式转换确实有效。让我们让它变得更复杂,如何使以下代码工作?

void f()
{
  std::vector<B> b_vector(4);
  std::vector<A> a_vector( b_vector.begin(), b_vector.end() );
}

UPDA是第三方库中的类,我不能编辑它的代码,所以我不能删除A的转换构造函数。

UPD:目前我找到的最简单的解决方案是为B定义转换构造函数的专业化。它可以在第三方库之外完成:

template<> A::A( const B &rhs ) : x_(rhs.c[0]), y_(rhs.c[1]) {}

原因是而不是只是因为它认为(A)bA(b)相同。该标准是关于显式类型转换的(5.4):

执行的转换

  • const_cast(5.2.11),

  • static_cast(5.2.9),

  • static_cast,后跟const_cast,

  • interpret_cast(5.2.10)或

  • interpret_cast后接const_cast,

可以使用铸件执行显式类型转换的表示法。相同的语义限制和行为适用。

本质上,这意味着即使对于(A)b的显式类型转换(即,如果您使用((A)b);来防止它成为变量声明)。它将使用CCD_ 11的规则。现在让我们来看看标准对static_cast(5.2.9):的描述

表达式e可以显式使用形式的static_caststatic_cast(e)如果声明对一些人来说,"T(e);"是很好的形式发明了临时变量t(8.5)。这种明确的效果转换与执行相同声明和初始化以及然后使用临时变量作为转换的结果。这个如果T是一个参考类型(8.3.2)和右值否则表达式e用作左值当且仅当初始化使用它作为左值。

如果你做static_cast<A>(b),它基本上可以看到A(b)是否形成良好;并且。仅仅因为模板函数副本构造函数的实际实例化失败,它并没有使实际声明格式错误,因此它使用了它并最终失败。

从5.4/1和5.4/5中,C-cast从列表中选择"最佳选择"C++cast。在这种情况下,这是一个static_cast

然后从5.2.9/2:

表达式e可以显式使用形式的static_caststatic_cast(e)如果声明对一些人来说,"T(e);"是很好的形式发明了临时变量t(8.5)。这种明确的效果转换与执行相同声明和初始化以及然后使用临时变量作为转换的结果。这个如果T是一个参考类型(8.3.2)和右值否则表达式e用作左值当且仅当初始化使用它作为左值。

因此,它甚至在尝试任何其他选项之前就选择了构造函数。

在这种情况下,您定义了两个转换以获得相同的最终结果,但该语言有特定的规则规定它将始终使用可用的构造函数。您可能应该离开构造函数,将运算符改为显式as类型的函数。

编辑OP的编辑:我认为您将无法使用向量iter, iter构造函数。您需要从一个空向量开始,然后使用带有push_back的for循环或使用std::transform

(A)b; // << here the error appears, compiler tries to use 

这是对A的显式强制转换。因此CCD_ 21的构造函数被调用以将CCD_ 22转换为CCD_。

A a(1,2);
a = b ; //this will invoke user-defined conversion of B (implicit conversion)

演示:http://www.ideone.com/K9IxT

如前所述,如果在格式良好的构造函数和转换运算符之间进行选择,编译器将始终调用构造函数。但如果你愿意,你可以直接打电话给转换运营商:

A a = b.operator A();

关于如何使其工作的问题的第二部分,我会使用std::transform,而不是在第三方库命名空间中修改/添加一些东西,除非只有这个库有很好的文档记录,并且您应该专门化这个构造函数:

a_vector.reserve( b_vector.size() );
std::transform( b_vector.begin(), b_vector.end(), 
  std::back_inserter( a_vector ), boost::bind( &B::operator A, _1 ) );

语法(A)bA(b)相同,后者是一种构造。例如,如果需要强制转换,则必须显式使用static_cast

相关文章: