2017-03-15 11 views
2

これは過去数時間にわたって私を夢中にさせてくれました。この問題を回避することはできません。私は、これらの60行のコード(メイン関数を含む)にまで問題を絞り込んだ。 別の名前空間とCRTPのテンプレートフレンド機能

#include <iostream> 

namespace n1 { 

// the general definition 
template <class X, class Y> void f(X&, const Y&) 
{ 
    std::cout << "general template definition.\n"; 
} 

} // namespace n1 
namespace n2 { 

// CRTP 
template <class Derived> class A 
{ 
    int data; 

    // partial function template specialization for n1::f, and declare 
    // it a friend too, so that it may access the data attribute of A 
    template <class Y> friend void n1::f(A<Derived>& a, const Y& y); 

}; // class A 

} // namespace n2 
namespace n1 { 

// implementation for this particular function template specialization 
template <class Derived, class Y> void f(n2::A<Derived>& a, const Y& y) 
{ 
    std::cout << "partial template specialization: " << a.data << "\n"; 
} 

} // namespace n1 
namespace n2 { 

// Another class! 
class B : public A<B> 
{ 

}; // class B  
} // namespace n2 
namespace n1 { 

// -------------------- 
// tricky part is here! 
// -------------------- 
template <class Y> void f(n2::B& b, const Y& y) 
{ 
    // FAIL! not a friend! How? 
    f(static_cast<n2::A<n2::B>&>(b), y); 
} 

} // namespace n1 

int main() 
{ 
    n2::B b; 
    int x; 
    n1::f(b, x); // should print "partial template specialization" 
    return 0; 
} 

だから、私が「欲しい」何それは A<Derived>の具象サブクラスで呼び出されるたびに、コンパイラは n1::fの私の関数テンプレートの特殊化を選択することです。コンパイラーが私の特殊化を支持するためには、すべてのサブクラス(この場合は B)に対して、単に呼び出しを委任する n1::fのテンプレート特殊化を指定する必要があります。その場合、私は n1::fA<Derived>の友人であると宣言しているので、 A<Derived>のメンバー変数 datan1::fにアクセスできると考えています。しかし、GCCは A<Derived>::dataがプライベートでアクセスできないと訴えている。 see this snippet on Coliru

この構成は可能ですか?もしそうなら、どうすればA<Derived>::dataにアクセスできないと不平を言うコンパイラを回避できますか? (公開することは選択肢ではありません)。

答えて

3

あなたのクラス定義は次のようになります。実際には

template <class Derived> class A 
{ 
    int data; 
    template <class D, class Y> friend void n1::f(A<D>& a, const Y& y); 
}; 

を、関数の宣言は次のとおりです。

template <class Derived, class Y> void f(n2::A<Derived>& a, const Y& y) 

あなたの友人の宣言ですがこの場合

template <class Y> friend void n1::f(A<Derived>& a, const Y& y); 

、彼らは違う獣なので、あなたはその誤りを受け取ります。ご覧のとおり、テンプレートパラメータリストは異なります。これは、分離された定義を持つ関数の宣言ではありません。それらは2つの異なる関数テンプレートであり、1つは宣言され、もう1つは宣言され、定義されています。
あなたのコードでは、友人の関数を宣言していますが、定義することは決してありません。反対側では、無料の関数テンプレートを導入しました。そのテンプレートは、dataのメンバーを読み取ることができません。この関数は、A<Derived>の友人ではありません。


wandboxで実行してください。

+0

私は見る...これは動作します!ワオ。余分なテンプレートパラメータ 'D'が*必須*である理由を説明できますか?私の友人宣言は、 'A 'を友人にすることができるように見えますが、それには別のテンプレートパラメータが必要だと思われます。 – rwols

+0

@rwols私は詳細を追加しました。それらは異なるテンプレートパラメータリストと異なる引数を持つため、2つの異なる関数テンプレートです。私はそれをよりよく説明する方法を知らない... – skypjack

+0

ありがとう、私はこれが私のために物事をクリアすると思います。 – rwols

関連する問題