比起转换构造函数,更喜欢转换运算符
prefer conversion operator over conversion constructor
我有以下代码片段:
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
的构造函数?如何使用B
对A
的转换运算符?
我使用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() );
}
UPD:A
是第三方库中的类,我不能编辑它的代码,所以我不能删除A
的转换构造函数。
UPD:目前我找到的最简单的解决方案是为B
定义转换构造函数的专业化。它可以在第三方库之外完成:
template<> A::A( const B &rhs ) : x_(rhs.c[0]), y_(rhs.c[1]) {}
原因是而不是只是因为它认为(A)b
与A(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)b
与A(b)
相同,后者是一种构造。例如,如果需要强制转换,则必须显式使用static_cast
。
- 如果条件不相关,我应该更喜欢两个 if 语句而不是 if-else 语句吗?
- 如何使科学记数法的转换更精确?
- 为什么 C++ 程序员更喜欢前缀 ++,而 Java 程序员更喜欢后缀 ++?
- 在C++ Lambda 表达式中,为什么人们更喜欢按值捕获而不是作为参数传递?
- 如果可能的话,C++总是更喜欢右值引用转换运算符而不是常量左值引用吗?
- 为什么斯科特·迈耶斯(Scott Meyers)建议更喜欢"迭代器"而不是"const_i
- 在实现文件中,我们应该更喜欢"using namespace"指令还是将实现包装在命名空间 { } 中?
- 为什么 clang++ 更喜欢 adcx 而不是 adc
- 如何让编译器在C++中更喜欢常量方法重载?
- 为什么重载解析更喜欢不受约束的模板函数而不是更具体的模板函数?
- 使用LLVM在代码生成期间,更喜欢LLVM :: StringMap或STD :: MAP
- 我应该更喜欢在函数中的常数:constexpr const或enum
- 如何更喜欢由AddFontMemresourceex加载的字体而不是系统
- 明智的选择是更喜欢lambdas功能对象
- 有什么理由更喜欢从 IDE 中运行应用程序而不是运行独立的可执行文件?
- 重载分辨率:调整 const/ref 是否不比用户定义的转换更好?
- 为什么使用Mesos代码比遗产更喜欢模板
- Curly Braces构造函数更喜欢initializer_list而不是更好的匹配.为什么
- 为什么有时人们更喜欢Strtoll而不是Strtol,即使他们只想将字符串转换为INT32
- 比起转换构造函数,更喜欢转换运算符