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*/>;

老实说,我看不出与所有可能的专长交朋友会如何导致问题,但让我们假设它确实如此。我知道的一种解决方法是使用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 回答