临时对象需要复制构造函数

发布于 2024-08-12 14:19:25 字数 1867 浏览 6 评论 0原文

以下代码仅在复制构造函数可用时才有效。

当我添加打印语句(通过 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

坠似风落 2024-08-19 14:19:25

来自 http://gcc.gnu.org/gcc-3.4/changes.html

绑定类类型的右值时
到引用,复制构造函数
类的内容必须是可访问的。为了
例如,考虑以下代码:

class A 
{
public:
  A();

private:
  A(const A&);   // private copy ctor
};

A makeA(void);
void foo(const A&);

void bar(void)
{
  foo(A());       // error, copy ctor is not accessible
  foo(makeA());   // error, copy ctor is not accessible

  A a1;
  foo(a1);        // OK, a1 is a lvalue
}

一开始这可能会令人惊讶
视线,尤其是因为最受欢迎
编译器没有正确实现
此规则(更多详细信息)。

这将通过 核心问题 391 在 C++1x 中修复

From http://gcc.gnu.org/gcc-3.4/changes.html

When binding an rvalue of class type
to a reference, the copy constructor
of the class must be accessible. For
instance, consider the following code:

class A 
{
public:
  A();

private:
  A(const A&);   // private copy ctor
};

A makeA(void);
void foo(const A&);

void bar(void)
{
  foo(A());       // error, copy ctor is not accessible
  foo(makeA());   // error, copy ctor is not accessible

  A a1;
  foo(a1);        // OK, a1 is a lvalue
}

This might be surprising at first
sight, especially since most popular
compilers do not correctly implement
this rule (further details).

This will be fixed in C++1x by Core Issue 391.

太阳男子 2024-08-19 14:19:25

这里标准的适用部分是 §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 给出错误消息是正确的,而其他人接受代码在技术上是错误的。我将您的代码简化为以下内容:

class N {
    public:
        N(int)  {}
    private:
        N(N const&);
};

void plop(N const& data) { }

int main() {
    plop(N(1));
}

当使用“--A”(严格错误模式)调用时,Comeau 给出以下错误消息:

"plop.cpp", line 12: error: "N::N(const N &)", required for copy that was
          eliminated, is inaccessible
      plop(N(1));
           ^

同样,当使用“/Za”(其“ANSI 一致性”模式)调用时,VC++ 9 给出:

plop.cpp
plop.cpp(12) : error C2248: 'N::N' : cannot access private member declared in class 'N'
        plop.cpp(6) : see declaration of 'N::N'
        plop.cpp(2) : see declaration of 'N'
        while checking that elided copy-constructor 'N::N(const N &)' is callable
        plop.cpp(6) : see declaration of 'N::N'
        when converting from 'N' to 'const N &'

我的猜测是大多数其他编译器的做法大致相同。由于它们优化了对复制构造函数的调用,因此通常不要求它存在或可访问。当您要求他们尽可能准确地遵守标准时,他们会给出错误消息,因为即使他们不使用它,这在技术上也是必需的。

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:

class N {
    public:
        N(int)  {}
    private:
        N(N const&);
};

void plop(N const& data) { }

int main() {
    plop(N(1));
}

When invoked with "--A" (strict errors mode), Comeau gives the following error message:

"plop.cpp", line 12: error: "N::N(const N &)", required for copy that was
          eliminated, is inaccessible
      plop(N(1));
           ^

Likewise, when invoked with "/Za" (its "ANSI conforming" mode), VC++ 9 gives:

plop.cpp
plop.cpp(12) : error C2248: 'N::N' : cannot access private member declared in class 'N'
        plop.cpp(6) : see declaration of 'N::N'
        plop.cpp(2) : see declaration of 'N'
        while checking that elided copy-constructor 'N::N(const N &)' is callable
        plop.cpp(6) : see declaration of 'N::N'
        when converting from 'N' to 'const N &'

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文