11

次のコードは、コピー コンストラクターが使用可能な場合にのみ機能します。

(経由で)printステートメントを追加std::coutし、コピーコンストラクターを使用可能にすると、それは使用されません(不要なコピーを削除するためにコンパイラーのトリックが発生していると思います)。

しかし、出力operator <<と以下の関数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-apple-darwin10
スレッド モデル: posix
gcc バージョン 4.2.1 (Apple Inc. ビルド 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)' の呼び出しに一致する関数がありません</p>

4

2 に答える 2

15

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
}

特に、ほとんどの一般的なコンパイラはこの規則を正しく実装していないため、これは一見すると驚くかもしれません (詳細)。

これは、 Core Issue 391によって C++1x で修正される予定です。

于 2009-12-01T15:48:38.577 に答える
5

ここでの標準の適用可能な部分は、参照の初期化をカバーする §8.5.3/5 と、右辺値と左辺値 (C++ では必ずしも明らかではない) を示す §3.10/6 です。

この場合、初期化式は「N(1)」であるため、関数表記法を使用して明示的にオブジェクトを作成しています。3.10/6 によると、その式は右辺値です。

次に、8.5.3/5 のルールを順番に見ていき、適用される最初のものを使用する必要があります。最初の可能性は、式が左辺値を表す場合、または暗黙的に左辺値に変換できる場合です。あなたの式は右辺値であり、左辺値への暗黙的な変換には参照を返す変換関数が必要になりますが、この場合は存在しないように見えるため、適用されないようです。

次の規則は、参照が const T でなければならないことを示しています (ここではこれが当てはまります)。この場合、式はクラス型の右辺値であり、参照と参照互換性があります (つまり、参照は同じクラスまたはクラスのベースに対するものです)。つまり、151 ページ (C++ 2003 PDF の 179) の下部にある箇条書きが適用されるようです。この場合、コンパイラは、参照を右辺値を表すオブジェクトに直接バインドするか、右辺値の一時コピーを作成してその一時コピーにバインドすることができます。

ただし、どちらの方法でも、標準では次のことが明示的に要求されています。

そのため、gcc がエラー メッセージを表示するのは正しく、他の 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 &'

私の推測では、他のほとんどのコンパイラーはほぼ同じことを行っていると思います。コピー コンストラクターへの呼び出しを最適化するため、通常、それが存在することやアクセス可能であることは必要ありません。できるだけ正確に標準に準拠するように依頼すると、エラーメッセージが表示されます。これは、使用していなくても技術的に必要であるためです。

于 2009-12-01T16:39:38.003 に答える