12

libstdc ++(4.6.1)を使用して、clang ++(clangバージョン3.1(トランク143100))でstd::shared_ptrを使用しようとしています。私は小さなデモプログラムを持っています:

#include <memory>

int main()
{
    std::shared_ptr<int> some(new int);
    std::shared_ptr<int> other(some);
    return 0;
}

以下を使用してビルドできます。

clang++ -std=c++0x -o main main.cpp

次のエラー出力が表示されます。

main.cpp:6:23: error: call to deleted constructor of 'std::shared_ptr<int>'
    std::shared_ptr<int> other(some);
                         ^     ~~~~
/usr/include/c++/4.6/bits/shared_ptr.h:93:11: note: function has been explicitly marked
deleted here
class shared_ptr : public __shared_ptr<_Tp>

何らかの理由で、移動コンストラクターが提供されているために削除されるコンストラクターが必要です (これは正しい動作です)。しかし、(g++ (Ubuntu/Linaro 4.6.1-9ubuntu3) 4.6.1.) でコンパイルできるのはなぜですか? これを修正する方法について誰かアイデアはありますか?

4

2 に答える 2

20

C++11 12.8p7 に従って、shared_ptr には移動コンストラクターまたは移動代入演算子 (またはその両方) があるため、shared_ptr の暗黙的に宣言されたコピー コンストラクターは削除されます。

クラス定義でコピー コンストラクターが明示的に宣言されていない場合は、暗黙的に宣言されます。クラス定義でムーブ コンストラクターまたはムーブ代入演算子が宣言されている場合、暗黙的に宣言されたコピー コンストラクターは削除済みとして定義されます。それ以外の場合は、デフォルト (8.4) として定義されます。

GCC 4.6.x はこのルールを実装していません。このルールは、N3203=10-0193としてプロセスの非常に遅い段階で C++11 ワーキング ペーパーに組み込まれました。libstdc++ 4.6.x の shared_ptr は書かれた時点では正しいものでしたが、その後 C++11 が変更されました.Boost には shared_ptr とまったく同じ問題があり、これはGCC と Clang の間の一般的な非互換性の 1 つです。

デフォルトのコピー コンストラクターとコピー代入演算子を shared_ptr に追加すると、問題が解決します。

于 2011-11-01T14:36:08.073 に答える
6

この場合、gcc 4.6 の標準ライブラリ ヘッダーは間違っているようです。標準では次のコンストラクタが必要なためです (§20.7.2.2.1/16)。

shared_ptr(const shared_ptr& r) noexcept;

これは、gcc 実装に欠けていると思われるコピー コンストラクターです。私が手元に持っている実装(g++-4.6.0)は(で)提供していますbits/shared_ptr.h

template<typename _Tp1>
shared_ptr(const shared_ptr<_Tp1>& __r)

ただし、適切なコピー コンストラクターがありません (テンプレート化されたコンストラクターは、コンパイラーによってコピー コンストラクターとして使用できません)。しかし、そのようなエラーが本番コンパイラで発生するのは奇妙だと思います...

編集私は、g ++ 4.6が上記のコードを独自の標準ライブラリでコンパイルする理由を理解しようとしています。コピー構築の実行可能なオーバーロードとして d コンストラクターを取り上げているようでtemplate、これがバグかどうかを確認するために標準を振り返りました。コピー コンストラクタかどうか。

C++03 標準では、脚注があります106) 。

テンプレート コンストラクターは決してコピー コンストラクターではないため、そのようなテンプレートが存在しても、コピー コンストラクターの暗黙の宣言は抑制されません。テンプレート コンストラクターは、コピー コンストラクターを含む他のコンストラクターとのオーバーロードの解決に参加します。テンプレート コンストラクターは、他のコンストラクターよりも適切に一致する場合、オブジェクトをコピーするために使用できます。

その脚注は C++11 にはありません。脚注は規範的ではないため、その脚注を裏付ける別の引用が必要です。その下にいくつかの段落があります。

12.8/3 クラス X のコンストラクターの宣言は、その最初のパラメーターが X 型 (オプションで cv 修飾されている) であり、他のパラメーターがないか、他のすべてのパラメーターに既定の引数がある場合、形式が正しくありません。メンバー関数テンプレートは、クラス オブジェクトをそのクラス型のオブジェクトにコピーするためにインスタンス化されることはありません。

段落の最初の部分は、コンストラクターが引数と同じ型を取ることができないことを意味します (コピー コンストラクターは参照を受け取り、そのようなコンストラクターを許可するとあいまいさが生じます。また、引数へのコピーが必要になるため、最適なオーバーロードが必要になるという事実もあります)。 --これがコピーコンストラクターを抑制したと仮定すると--同じ関数になり、それには...無限ループが必要になります)。

条項の 2 番目の部分では、テンプレート化されたコンストラクターを使用して型のコピーを作成することはできないと述べています。これは、脚注106)をサポートする規範的なセクションのようです。現在、これは C++11 で慎重に書き直され、次のように述べられています。

12.8/6 クラス X のコンストラクターの宣言は、その最初のパラメーターが X 型 (オプションで cv 修飾) であり、他のパラメーターがないか、他のすべてのパラメーターにデフォルト引数がある場合、形式が正しくありません。メンバー関数テンプレートは、そのようなコンストラクター シグネチャを生成するためにインスタンス化されることはありません。

さて、これが意味することは、テンプレートを使用してコピーできないという制限が削除され、より厳密でない制限に置き換えられたことです: テンプレートはフォームのコンストラクターを生成するためにインスタンス化されませんS( S )。コピー コンストラクター。

このより少ない制限により、上記のテンプレート化されたコンストラクターは、生成される署名に互換性があるため、実際にはcopy-constructorとして使用できるようです。bits/shared_ptr.hこれは、ヘッダーを処理するときの g++ 4.6 コンパイラの動作をサポートしておりclang++、テンプレートを有効なコンストラクタとして使用できていないことを意味します。

次の問題は、g++4.6 に同梱されている標準ライブラリの実装が実際に準拠しているかどうかです。私は頭の上から言うことはできません。一方では、前述のコンストラクターの署名が欠落しているため、準拠していないと主張できます。しかし一方で、準拠したコンパイラは、同じ機能を実現するためにテンプレート化されたコンストラクターを取得する必要があり、実装はそのコンストラクターが存在するかのように動作します。

于 2011-11-01T09:13:28.257 に答える