5

これは、C++ 標準に関する質問です。次のコードを検討してください。

template <typename T>
class has_Data
{
   typedef char one;
   typedef long two;

   template <typename C> static one test( typeof(&C::Data) ) ;
   template <typename C> static two test(...);

public:
   enum { value = sizeof(test<T>(0)) == sizeof(char) };
};

class MyClass {
private:
   struct Data {
   };
};


void function(bool val = has_Data<MyClass>::value) {}

上記のコードはgcc (GCC) 4.4.3

ただし、clang version 3.3 (2545b1d99942080bac4a74cda92c620123d0d6e9) (2ff97832e593926ea8dbdd5fc5bcf367475638a9)

次のエラーが発生します。

test_private_data.cpp:7:54: error: 'Data' is a private member of 'MyClass'
   template <typename C> static one test( typeof(&C::Data) ) ;
                                                     ^
/devshared/home/rhanda/test_private_data.cpp:7:37: note: while substituting explicitly-specified template arguments into function template 'test'
   template <typename C> static one test( typeof(&C::Data) ) ;
                                    ^
/devshared/home/rhanda/test_private_data.cpp:21:26: note: in instantiation of template class 'has_Data<MyClass>' requested here
void function(bool val = has_Data<MyClass>::value) {}
                         ^
1 error generated.

どちらが正しいですか?

標準文書 ( n3485 ) から、gcc よりも clang と一致すると思われるステートメントを見つけました。

アクセス制御は、名前が宣言または式から参照されるかどうかに関係なく、すべての名前に一様に適用されます。

4

1 に答える 1

3

GCCが正しいと思います。

最初に注意すべきことは、非friendコードは特定のプライベート メンバーの存在を積極的に報告できるべきではないということです。それがあなたがやろうとしていることなら、あなたはあなたのデザインを修正しなければなりません. クラスはプライベート メンバーを使用して何でも行うことができ、他のコード (フレンドを除く) はそれを知る方法がありません。それは設計によるものです。

ただし、SFINAEの原則があります。置換の失敗はエラーではありません。は非公開であるためMyClass::Data、コードはhas_Data(私の意見では)C::Dataメンバーがまったく存在しないかのように動作する必要があります。したがって、最初の関数は置換の失敗につながり、黙って無視され、2 番目の関数が使用されます。もう少しコードを追加すると、私の GCC 4.7.2 はこれを問題なくコンパイルし、has_Data<MyClass>::value評価するとfalse. 私の意見では、SFINAEを修正してください。

あなたが参照したドキュメントからの引用でこの意見を裏付けようとしたところ、セクション 14.8.2 パラグラフ 8 で次のことがわかりました。

注: アクセス チェックは、置換プロセスの一部として実行されます。

これは標準の非規範的な注記ですが、GCC が処理する方法と同じように、SFINAE が実際にこの状況に適用されるべきであることを非常に読みやすく明確に示しているように思えます。

編集: @hvdがコメントで指摘したように、上記は C++11 にのみ当てはまります。標準の古いバージョンでは、状況が異なっていました。問題 1170: テンプレート引数推定中のアクセス チェックに、その変更に関する詳細が含まれています。

GCCは、GNU 拡張機能を使用して、-std=c++03またはその-std=c++11ために、このコードをコンパイルしません。typeofそれでもコードをコンパイルするという事実-std=gnu++03はおそらく不適切と見なされるかもしれませんが、今後は C++11 のセマンティクスを使用することになるため、これについてレポートを提出することはありません。

于 2013-04-25T08:47:50.263 に答える