临时对象需要复制构造函数
以下代码仅在复制构造函数可用时才有效。
当我添加打印语句(通过 std::cout)并使复制构造函数可用时,它不会被使用(我假设存在编译器技巧来删除不必要的副本)。
但在下面的输出运算符 << 和函数 plop() 中(我在其中创建临时对象),我认为不需要复制构造函数。有人可以解释为什么当我通过 const 引用传递所有内容时语言需要它(或者我做错了什么)。
#include <iostream>
class N
{
public:
N(int) {}
private:
N(N const&);
};
std::ostream& operator<<(std::ostream& str,N const& data)
{
return str << "N\n";
}
void plop(std::ostream& str,N const& data)
{
str << "N\n";
}
int main()
{
std::cout << N(1); // Needs copy constructor (line 25)
plop(std::cout,N(1)); // Needs copy constructor
N a(5);
std::cout << a;
plop(std::cout,a);
}
编译器:
[Alpha:~/X] myork% g++ -v
使用内置规格。
目标:i686-apple-darwin10
配置为: /var/tmp/gcc/gcc-5646~6/src/configure --disable-checking --enable-werror --prefix=/usr --mandir=/share/man --enable-languages=c ,objc,c++,obj-c++ --program-transform-name=/^[cg][^.-]*$/s/$/-4.2/ --with-slibdir=/usr/lib --build= i686-apple-darwin10 --with-gxx-include-dir=/include/c++/4.2.1 --program-prefix=i686-apple-darwin10- --host=x86_64-apple-darwin10 --target=i686-苹果-darwin10
线程模型:posix
gcc 版本 4.2.1(Apple Inc. build 5646)[Alpha:~/X] myork%g++ t.cpp
t.cpp:在函数“int main()”中:
t.cpp:10: 错误:'N::N(const N&)' 是私有的
t.cpp:25: 错误:在此上下文中
t.cpp:10: 错误:'N::N(const N&)' 是私有的
t.cpp:26: 错误:在此上下文中
这是一些真实代码的简化版本。
在实际代码中,我有一个包含 std::auto_ptr 的类。这意味着采用 const 引用的复制构造函数无效(无需进行一些工作),并且我收到一条错误,表明复制构造函数因此不可用:
也更改类:
class N
{
public:
N(int) {}
private:
std::auto_ptr<int> data;
};
然后错误是:
t.cpp:25: 错误:没有匹配的函数可用于调用“N::N(N)”
The following code only works when the copy constructor is available.
When I add print statements (via std::cout
) and make the copy constructor available it is not used (I assume there is so compiler trick happening to remove the unnecessary copy).
But in both the output operator <<
and the function plop()
below (where I create a temporary object) I don't see the need for the copy constructor. Can somebody explain why the language needs it when I am passing everything by const reference (or what I am doing wrong).
#include <iostream>
class N
{
public:
N(int) {}
private:
N(N const&);
};
std::ostream& operator<<(std::ostream& str,N const& data)
{
return str << "N\n";
}
void plop(std::ostream& str,N const& data)
{
str << "N\n";
}
int main()
{
std::cout << N(1); // Needs copy constructor (line 25)
plop(std::cout,N(1)); // Needs copy constructor
N a(5);
std::cout << a;
plop(std::cout,a);
}
Compiler:
[Alpha:~/X] myork% g++ -v
Using built-in specs.
Target: i686-apple-darwin10
Configured with: /var/tmp/gcc/gcc-5646~6/src/configure --disable-checking --enable-werror --prefix=/usr --mandir=/share/man --enable-languages=c,objc,c++,obj-c++ --program-transform-name=/^[cg][^.-]*$/s/$/-4.2/ --with-slibdir=/usr/lib --build=i686-apple-darwin10 --with-gxx-include-dir=/include/c++/4.2.1 --program-prefix=i686-apple-darwin10- --host=x86_64-apple-darwin10 --target=i686-apple-darwin10
Thread model: posix
gcc version 4.2.1 (Apple Inc. build 5646)[Alpha:~/X] myork% g++ t.cpp
t.cpp: In function ‘int main()’:
t.cpp:10: error: ‘N::N(const N&)’ is private
t.cpp:25: error: within this context
t.cpp:10: error: ‘N::N(const N&)’ is private
t.cpp:26: error: within this context
This is a simplified version of some real code.
In the real code I have a class that contains a std::auto_ptr. This means that a copy constructor that takes a const reference is not valid (without some work) and I was getting an error indicating that the copy constructor was not available because of it:
Change the class too:
class N
{
public:
N(int) {}
private:
std::auto_ptr<int> data;
};
The error is then:
t.cpp:25: error: no matching function for call to ‘N::N(N)’
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
来自 http://gcc.gnu.org/gcc-3.4/changes.html
这将通过 核心问题 391 在 C++1x 中修复。
From http://gcc.gnu.org/gcc-3.4/changes.html
This will be fixed in C++1x by Core Issue 391.
这里标准的适用部分是 §8.5.3/5,它涵盖了引用的初始化和 §3.10/6,它告诉我们什么是右值和什么是左值(在 C++ 中并不总是很明显)。
在本例中,初始化表达式为:“N(1)”,因此您使用函数表示法显式创建对象。根据 3.10/6,该表达式是一个右值。
然后我们必须按顺序浏览 8.5.3/5 中的规则,并使用第一个适用的规则。第一种可能性是表达式是否表示左值,或者可以隐式转换为左值。您的表达式是一个右值,隐式转换为左值将需要一个返回引用的转换函数,在本例中似乎不存在,因此这似乎不适用。
下一条规则规定引用必须是 const T(这里就是这种情况)。在这种情况下,表达式是类类型的右值,并且与引用是引用兼容的(即引用是对同一个类或该类的基类)。这意味着第 151 页底部的项目符号(C++ 2003 PDF 的第 179 页)似乎适用。在这种情况下,编译器可以将引用直接绑定到表示右值的对象,或者创建右值的临时副本,然后绑定到该临时副本。
然而,无论哪种方式,标准都明确要求:“无论复制是否实际完成,用于制作副本的构造函数都应该是可调用的。”
因此,我相信 gcc 给出错误消息是正确的,而其他人接受代码在技术上是错误的。我将您的代码简化为以下内容:
当使用“--A”(严格错误模式)调用时,Comeau 给出以下错误消息:
同样,当使用“/Za”(其“ANSI 一致性”模式)调用时,VC++ 9 给出:
我的猜测是大多数其他编译器的做法大致相同。由于它们优化了对复制构造函数的调用,因此通常不要求它存在或可访问。当您要求他们尽可能准确地遵守标准时,他们会给出错误消息,因为即使他们不使用它,这在技术上也是必需的。
The applicable parts of the standard here are §8.5.3/5, which covers initialization of references and §3.10/6, which tells what's an rvalue and what's an lvalue (not always obvious in C++).
In this case, you initialization expression is: "N(1)", so you're explicitly creating an object using functional notation. According to 3.10/6, that expression is an rvalue.
Then we have to walk through the rules in 8.5.3/5 in order, and use the first that applies. The first possibility is if the expression represents an lvalue, or can be implicitly converted to an lvalue. Your expression is an rvalue, and implicit conversion to an lvalue would require a conversion function that returns a reference, which doesn't seem to exist in this case, so that doesn't seem to apply.
The next rule says the reference must be to a const T (which is the case here). In this case, the expression is an rvalue of class type and is reference-compatible with the reference (i.e. the reference is to the same class, or a base of the class). That means the bullet at the bottom of page 151 (179 of the C++ 2003 PDF) seems to apply. In this case, the compiler is allowed to either bind the reference directly to the object representing the rvalue, OR create a temporary copy of the rvalue, and bind to that temporary copy.
Either way, however, the standard explicitly requires that: "The constructor that would be used to make the copy shall be callable whether or not the copy is actually done."
As such, I believe that gcc is right to give an error message, and the others are technically wrong to accept the code. I simplified your code a bit to the following:
When invoked with "--A" (strict errors mode), Comeau gives the following error message:
Likewise, when invoked with "/Za" (its "ANSI conforming" mode), VC++ 9 gives:
My guess is that most of the other compilers do roughly the same. Since they optimize out the call to the copy constructor, they don't normally require that it exist or be accessible. When you ask them to conform to the standard as accurately as they can, they give the error message, because it's technically required even though they don't use it.