代码之家  ›  专栏  ›  技术社区  ›  Walter

解决模板友元函数的问题

  •  2
  • Walter  · 技术社区  · 7 年前

    template<typename T>
    struct foo
    {
        using type = T;
        friend type bar(foo const& x) { return x.X; }
        foo(type x) : X(x) {}
      private:
        type X;
    };
    
    template<typename> struct fun;
    template<typename T> fun<T> bar(foo<T> const&, T);  // forward declaration
    
    template<typename T>
    struct fun
    {
        using type = T;
        friend fun bar(foo<type> const& x, type y)
        { return {bar(x)+y}; }
      private:
        fun(type x) : X(x) {}
        type X;
    };
    
    int main()
    {
        foo<int> x{42};
        fun<int> y = bar(x,7);    // called here
    };
    

    编译器需要forward声明来解析中的调用 main() (见 this answer 原因)。但是,编译器现在在链接/加载阶段会抱怨:

    未定义的体系结构符号x86\u 64:“乐趣 _main in foo-00bf19.o ld:未找到架构x86\u 64的符号

    即使函数是在友元声明中定义的。如果我把定义从 struct func<> ,即。

    template<typename T>
    struct fun
    {
        using type = T;
        friend fun bar(foo<type> const& x, type y);
      private:
        fun(type x) : X(x) {}
        type X;
    };
    
    template<typename T>
    inline fun<T> bar(foo<T> const& x, T y)
    { return {bar(x)+y}; }
    

    编译失败,原因是

    foo.cc:29:10: error: calling a private constructor of class 'fun<int>'
    { return {bar(x)+y}; }
    

    那么,我怎样才能让它工作呢(编译器:Apple LLVM版本9.0.0(clang-900.0.39.2),c++11)

    4 回复  |  直到 7 年前
        1
  •  2
  •   user7860670    7 年前

    fun内的Friend声明必须与函数模板转发声明匹配,否则会产生不相关的函数:

    template<typename TT> fun<TT> friend ::bar(foo<TT> const &, TT);
    

    定义应该放在外面:

    template<typename T> fun<T> bar(foo<T> const& x, T y)
    { return {bar(x)+y}; }
    

    online compiler

    演示问题的较短代码是:

    void foo(void);
    
    template<typename T>
    struct bar
    {
        friend void foo(void) {}
    };
    
    int main()
    {
        foo(); // undefined reference to `foo()'
        return 0;
    }
    

    17.8.1隐式实例化[temp.inst]

    1. 类模板专门化的隐式实例化导致声明的隐式实例化,而不是类成员函数、成员类、作用域成员枚举、静态数据成员、成员模板和 friends
        2
  •  2
  •   Yakk - Adam Nevraumont    7 年前

    关于友元函数有一些奇怪的规则。

    namespace X {
      template<class T>
      struct A{
        friend void foo(A<T>) {}
      };
    }
    

    foo 以上不是模板函数。它是一个非模板友元函数,存在于包含 A 但只能通过ADL查找找到;它不能直接命名为 X::foo .

    很像模板的成员可以是模板本身,也可以不是,这是一个非模板友元函数,它是为模板的每个模板类实例化创建的 A .

    namespace X{
      template<class T>
      void foo(A<T>);
    }
    

    在命名空间中 X 作为非模板友元函数

    从这一点来看,你的大部分错误都很清楚。您认为的转发声明是一个不相关的模板函数。你认为是朋友的东西不是,所以你没有访问私有构造函数的权限。

    我们可以用几种方法解决这个问题。我最喜欢的方法是添加标记类型。

    template<class T>struct tag_t{using type=T;};
    template<class T>
    constexpr tag_t<T> tag{};
    

    tag

    template<typename T>
    struct fun {
      using type = T;
      friend fun bar(tag_t<fun>, foo<type> const& x, type y)
      { return {bar(x)+y}; }
    private:
      fun(type x) : X(x) {}
      type X;
    };
    

    然后主要是:

    foo<int> x{42};
    fun<int> y = bar(tag<fun<int>>, x, 7);    // called here
    

    但你可能不想提 tag<fun<int>> ,所以我们只创建一个非朋友 bar

    template<class T>
    fun<T> bar(foo<T> const& x, type y)
    { return bar( tag<T>, x, y ); }
    

    酒吧 找到了。

    template 功能 酒吧 fun

        3
  •  1
  •   Walter    7 年前

    好吧,我找到了答案:

    为了使友元声明正常工作,必须将其限定为函数 模板

    template<typename T>
    struct fun
    {
        using type = T;
        friend fun bar<T>(foo<type> const& x, type y);
        //            ^^^
      private:
        fun(type x) : X(x) {}
        type X;
    };
    
    template<typename T>
    inline fun<T> bar(foo<T> const& x, T y)
    { return {bar(x)+y}; }
    

    但是,将定义与友元声明相结合仍然失败:

    template<typename T>
    struct fun
    {
        using type = T;
        friend fun bar<T>(foo<type> const& x, type y)
        { return {bar(x)+y}; }
      private:
        fun(type x) : X(x) {}
        type X;
    };
    

    结果:

    foo.cc:20:16: warning: inline function 'bar<int>' is not defined [-Wundefined-inline]
        friend fun bar<T>(foo<T> const& x, T y)
                   ^
    1 warning generated.
    Undefined symbols for architecture x86_64:
      "fun<int> bar<int>(foo<int> const&, int)", referenced from:
          _main in foo-c4f1dd.o
    ld: symbol(s) not found for architecture x86_64
    

    我真的不明白,因为远期申报仍然有效。

        4
  •  1
  •   ypnos    7 年前

    template<typename T>
    struct foo
    {
        using type = T;
        friend type bar(foo const& x) { return x.X; }
        foo(type x) : X(x) {}
      private:
        type X;
    };
    
    template<typename> struct fun;
    template<typename T> fun<T> bar(foo<T> const&, T);  // forward declaration
    
    template<typename T>
    struct fun
    {
        using type = T;
        friend fun bar<type>(foo<type> const& x, type y);
      private:
        fun(type x) : X(x) {}
        type X;
    };
    
    template<typename T>
    inline fun<T> bar(foo<T> const& x, T y)
    { return {bar(x)+y}; }
    
    int main()
    {
        foo<int> x{42};
        fun<int> y = bar(x,7);    // called here
    };
    

    使用GCC 8.2.1。我添加的是朋友声明的模板名称。