c++ - C2440:“正在初始化”:无法从“A”转换'到'一个'
问题描述
此代码在 Visual Studio 2017 中引发编译错误:
#include <iostream>
#include <string>
using std::cin;
using std::cout;
template<class T>
class A
{
public:
A(T a);
~A() {}
#if 0
A(const A<T>&);
#else
A(A<T>&);
#endif
T t;
};
template<class T>
A<T>::A(T a) : t(a) {}
template <class T>
#if 0
A<T>::A(const A<T>& a)
#else
A<T>::A(A<T>& a)
#endif
{
t = a.t;
std::cout << "In A copy constructor.\n";
}
int main()
{
std::string s;
A<int> a1(11);
A<double> a2(2.71);
#if 1
A<double> a3 = A<double>(a2); //gives C2440 when copy constructor argument is not const.
//compiler message is: 'initializing': cannot convert from 'A<double>' to 'A<double>'
#else
A<double> a3{a2}; //works when copy constructor argument is not const.
#endif
std::cout << a3.t << "\n";
std::cout << "Press ENTER to exit.\n";
std::getline(std::cin, s);
}
编译失败并出现 C2440: 'initializing': cannot convert from 'A<double>' to 'A<double>'.
当前两个#if 0
s 更改为#if 1
s(使用 const 参数选择复制构造函数)时,程序编译并运行。此外,如果#if 0
为所有条件编译选择了,则程序编译并运行。
这个问题没有我的答案。根据 cppreference.com,具有非常量参数的复制构造函数是可能的:
类 T 的复制构造函数是一个非模板构造函数,其第一个参数是 T&、 const T&、volatile T&或 const volatile T&,或者没有其他参数,或者其余参数都有默认值。
当我写的时候,带有非常量参数的复制构造函数仍然有效
A<double> a3{a2};
那么为什么初始化
A<double> a3 = A<double>(a2);
当复制构造函数的参数不是 const 时不起作用?
解决方案
在这种情况下,特别缺少 MSVC 错误消息;如果你在上面运行 GCC,你会得到以下错误:
main.cpp: In function ‘int main()’:
main.cpp:42:20: error: cannot bind non-const lvalue reference of type ‘A<double>&’ to an rvalue of type ‘A<double>’
42 | A<double> a3 = A<double>(a2); //gives C2440 when copy constructor argument is not const.
| ^~~~~~~~~~~~~
main.cpp:28:15: note: initializing argument 1 of ‘A<T>::A(A<T>&) [with T = double]’
28 | A<T>::A(A<T>& a)
| ~~~~~~^
make: *** [<builtin>: main] Error 1
如果您去掉示例中所有不相关的部分,您将得到:
int main() {
double &a = 2.71;
}
仍然返回相同的错误:
main.cpp: In function ‘int main()’:
main.cpp:2:13: error: cannot bind non-const lvalue reference of type ‘double&’ to an rvalue of type ‘double’
2 | double &a = 2.71;
| ^~~~
make: *** [<builtin>: main] Error 1
要解开这个问题,我们需要查看 C++ 中不同的值类别以及每个类别有哪些限制:
从广义上讲, An
lvalue
是有名字的东西。在您最初失败的示例中,lvalue
是A<T> &a
复制构造函数的参数,而在我的精简示例中,它是double &a
.An
rvalue
有点棘手,但它实际上是没有名字的东西。通常它被称为 atemporary
(正如@Eljay 在他们的评论中所做的那样),因为没有名称,它没有生命周期,所以它几乎立即被破坏(如下所述的警告)。在您的示例中,rvalue
isA<double>(a2)
,而在我的示例中, is2.71
。
链接的 cppreference 页面有两个部分与rvalue
此处相关:
- 右值的地址不能被内置的地址操作符获取:、、、、
&int()
和是无效的&i++
&42
&std::move(x)
- 右值可用于初始化 const 左值引用,在这种情况下,由右值标识的对象的生命周期会延长,直到引用范围结束
第一点是导致复制构造函数出现问题的原因,而没有const
传递rvalue
; 根据语言规则,被引用的对象在构造函数中不再有效。
第二点是允许复制构造函数const
在传递rvalue
;时进行操作的原因。生命周期被延长,直到构造函数内部的引用结束,这允许构造函数复制成员变量等。
复制消除皱纹
您初始化的两种方式a3
与第一种不同:
A<double> a3{a2};
lvalue
仅使用( )调用复制构造函数一次,a2
而第二次:
A<double> a3 = A<double>(a2);
调用复制构造函数两次,第一次使用上面的lvalue
( a2
),第二次使用 an rvalue
(第一次构造函数调用的结果)。但是,复制省略优化将删除其中一个调用,乍一看可能会令人困惑,因为这两个复制构造函数调用中的哪一个导致问题可能并不明显。
推荐阅读
- python - Python:如何在 RGB 图像上实现二进制过滤器?(算法)
- python - Pandas 通过 groupby 日期的总和对由 datetimeindex 索引的列进行归一化
- mysql - 带有 MySQL 的 Raspbian Spin Docker 容器
- python - 在只包含常量的文件中不使用类的优点/缺点?
- javascript - 如何从节点控制台调用异步方法?
- swift - 有没有更好的方法来构建许多不同的 URL?
- xml - 在 VS 2015 片段中添加多次 $selected$
- python - 尝试从多个帧创建视频
- zlib - 放气的 zlib 对象有多大?
- xcode - xcode 10,unity,vuforia Command CodeSign 失败,退出代码为非零