13

テンプレート化されたクラスのフレンドをデフォルトの引数で定義するには、以下のコードのようにすべてのフレンドを指定する必要がありますか?

// Different class implementations
enum ClassImplType { CIT_CHECK, CIT_FAST, CIT_GPU, CIT_SSE, CIT_NOF_TYPES } ;

// Graph class has default template argument CIT_CHECK
template <typename T, ClassImplType impl_type = CIT_CHECK>
class graph {
  //...
};

// Vertex class
template <typename T>
class vertex {
  //...
  friend class graph<T, CIT_CHECK>;
  friend class graph<T, CIT_FAST>;
  friend class graph<T, CIT_GPU>;
  friend class graph<T, CIT_SSE>;
};

可能なすべての ClassImplType 列挙値に対してフレンドが定義されていることを示す、より短い方法があると想像できます。のようなものfriend class graph<T, ClassImplType>ですが、後者はもちろん機能しません。

私が使用する用語が間違っている場合はお詫び申し上げます。

4

4 に答える 4

8

可能なすべての ClassImplType 列挙値に対してフレンドが定義されていることを示す、より短い方法があると想像できます。

残念ながら、実際にはありません。あなたは試してみてください

template<ClassImplType I> friend class graph<T, I>;

しかし、標準では、部分的な専門化と仲良くすることを単に禁止しています。

§14.5.4 [temp.friend] p8

フレンド宣言は、部分的な特殊化を宣言してはなりません。【例:

template<class T> class A { };
class X {
  template<class T> friend class A<T*>; // error
};

—終わりの例]

あなたはそれらすべてを友達にすることしかできません:

template<class U, ClassImplType I>
friend class graph;

または特定のもの:

friend class graph<T /*, optional-second-arg*/>;

正直に言うと、考えられるすべてのスペシャライゼーションと仲良くすることがここで問題を引き起こす可能性があることはわかりませんが、そうであると仮定しましょう. 私が知っている 1 つの回避策はpasskey patternを使用することですが、少し縮小したバージョンを使用します (allowテンプレートのすべての特殊化へのアクセスを許可するにはうまく機能しないため、ここではメカニズムを使用できません)。

template<class T>
class passkey{    
  passkey(){}
  friend T;

  // optional
  //passkey(passkey const&) = delete;
  //passkey(passkey&&) = delete;
};

// Different class implementations
enum ClassImplType { CIT_CHECK, CIT_FAST, CIT_GPU, CIT_SSE, CIT_NOF_TYPES } ;

template<class> struct vertex;

// Graph class has default template argument CIT_CHECK
template <typename T, ClassImplType impl_type = CIT_CHECK>
class graph {
public:
  void call_f(vertex<T>& v){ v.f(passkey<graph>()); }
  //...
};

// Vertex class
template <typename T>
class vertex {
  //...
public:
  template<ClassImplType I>
  void f(passkey<graph<T,I>>){}
};

テスト付きの実例。

アクセスする必要があるすべての機能を公開する必要があることに注意してください。ただし、指定された専門graph化によってのみ作成できるパスキーのおかげで、それは問題ではありません。graph

さらに進んで、頂点機能にアクセスするために使用できるプロキシ クラスを作成することもできます (graph変更のみ)。

// Graph class has default template argument CIT_CHECK
template <typename T, ClassImplType impl_type = CIT_CHECK>
class graph{
  typedef passkey<graph> key;
  // proxy for succinct multiple operations
  struct vertex_access{
    vertex_access(vertex<T>& v, key k)
      : _v(v), _key(k){}

    void f(){ _v.f(_key); }

  private:
    vertex<T>& _v;
    key _key;
  };

public:
  void call_f(vertex<T>& v){
    vertex_access va(v, key());
    va.f(); va.f(); va.f();
    // or
    v.f(key());
  }
  //...
};

実例。

于 2012-10-24T20:54:02.567 に答える
6

friendステートメントをテンプレート化できます。

template<typename U, ClassImplType  V>
friend class graph_foo;

を維持する方法Tを見つけようとしています-見つけたら更新します。

于 2012-10-24T18:31:01.543 に答える
3

再帰継承を使用して「修正」する次の方法を考えました。

インライン コメントは、何が起こっているかを説明します。

#include <type_traits>
#include <string>
#include <iostream>
#include <typeinfo>

// Different class implementations
enum ClassImplType { CIT_CHECK, CIT_FAST, CIT_GPU, CIT_SSE, CIT_NOF_TYPES };

template <typename T, ClassImplType impl_type = CIT_CHECK>
class graph;

// Vertex class
namespace impl
{
    template <typename, ClassImplType, typename enabler = void> struct vertex_impl;

    ///////////////////////////////////////////////////////////////
    // actual implementation (stop condition of recursion)
    static const ClassImplType CIT_ENDMARKER = (ClassImplType) -1;

    template <typename T> struct vertex_impl<T, CIT_ENDMARKER> 
    {
        protected: // make it protected rather than private
            int secret() const { return 42; }
    };

    ///////////////////////////////////////////////////////////////
    // recursion, just to mark friends
    template <typename T, ClassImplType impl_type>
        struct vertex_impl<T, impl_type, typename std::enable_if<CIT_ENDMARKER != impl_type>::type>
            : public vertex_impl<T, ClassImplType(impl_type - 1)>
        {
             friend class ::graph<T, impl_type>;
        };
}

///////////////////////////////////////////////////////////////
// Public typedef
#if 1
    template <typename T> struct vertex : impl::vertex_impl<T, CIT_NOF_TYPES> { };
#else // or c++11
    template <typename T> using vertex = impl::vertex_impl<T, CIT_NOF_TYPES>;
#endif

template <typename T, ClassImplType impl_type>
class graph
{
    public:
        static void TestFriendOf(const vertex<T>& byref)
        {
            std::cout << byref.secret() << std::endl;
        }
};

int main(int argc, const char *argv[])
{
    vertex<int> t;

    graph<int, CIT_CHECK>     :: TestFriendOf(t);
    graph<int, CIT_FAST>      :: TestFriendOf(t);
    graph<int, CIT_GPU>       :: TestFriendOf(t);
    graph<int, CIT_SSE>       :: TestFriendOf(t);
    graph<int, CIT_NOF_TYPES> :: TestFriendOf(t);
}

これは gcc と clang で動作します。

http://liveworkspace.org/code/f03c0e25a566a4ca44500f4aaecdd354で実際にご覧ください

PS。これは一見冗長性の問題を正確に解決するものではありませんが、解決策をより一般的にして、ハードコーディングされたものではなく別の「ベースケース」から継承することができます。 (グラフ型のファミリーと仲良くする必要がある多くのクラスがある場合)

于 2012-10-24T20:11:06.003 に答える
2

この「バグ以外の」説明によると、クラスは部分的な特殊化の友達になることはできません:http: //gcc.gnu.org/bugzilla/show_bug.cgi?id =5094

 What clenches it is that 14.5.3, p9
 explicitly prohibits friend declarations of partial specializations:

   -9- Friend declarations shall not declare partial specializations.
       [Example:
           template<class T> class A { };
           class X {
               template<class T> friend class A<T*>; // error
           };
       容nd example]

しかし、私は解決策にたどり着きました。それは完璧に見えず、使いやすいものでもありません。アイデアは、「友情」を外部クラスに転送するためだけに、中間の友人内部クラスを作成することです。欠点(または利点?)は、外部の友人が利用できるすべての関数/メンバー変数をラップする必要があることです。

// Different class implementations
enum ClassImplType { CIT_CHECK, CIT_FAST, CIT_GPU, CIT_SSE, CIT_NOF_TYPES } ;

template <typename T>
class vertex;

// Graph class has default template argument CIT_CHECK
template <typename T, ClassImplType impl_type = CIT_CHECK>
class graph {
    typedef typename vertex<T>::template graph_friend<impl_type> graph_friend;
public:
  graph(vertex<T>& a) { graph_friend::foo(a); } // here call private method    
  //...
};

// Vertex class
template <typename T>
class vertex {
  //...
  int foo() {}
public:
  template <ClassImplType impl_type>
  class graph_friend {
     static int foo(vertex& v) { return v.foo(); }
     friend class graph<T,impl_type>;
  };

};
int main() {
    vertex<int> a;
    graph<int,CIT_SSE> b(a);

}
于 2012-10-24T19:57:38.473 に答える