命名空间中的类并使用模板类型作为返回类型时的全局范围友元运算符声明

Posted

技术标签:

【中文标题】命名空间中的类并使用模板类型作为返回类型时的全局范围友元运算符声明【英文标题】:Global scope friend operator declaration when class in namespace and using templated type as return type 【发布时间】:2022-01-23 22:40:00 【问题描述】:

我正在为模板化运算符和命名空间的朋友声明而苦苦挣扎。 对不起,如果我有点长,但我想很好地描述我的问题。

首先,一些上下文。目前忘记命名空间。 我有一个类 A 和一个需要访问其私有成员的公共运算符:

template<typename U>
struct B  U valb; ;

template<typename U>
struct C  U valc; ;

template<typename U,typename V>
struct A
 
  private:
    U v1; V v2;

  template<typename T1,typename T2>
  friend A<T1,T2> operator * ( const B<T2>&, const C<T1>& );
;

template<typename T1,typename T2>
A<T1,T2>
operator * ( const B<T2>& b, const C<T1>& c )

    A<T1,T2> a;
    a.v1 = c.valc * b.valb; // dummy
    return a;


int main()

    B<float> b;
    C<int> c;
    auto a = b * c;

这构建得很好。

现在出于某种原因,我想将类 A 放在命名空间中,(主要是为了从公共 API 中删除它,用户代码将使用“子”类型,用 using 声明声明)。 现在麻烦开始了。

我正在构建 this answer,它涵盖了该主题并且工作正常。 它解释了我需要转发声明类,然后是运算符,并在朋友声明中,为运算符添加前缀::

该链接问题中描述的情况与我的情况之间的唯一区别是返回类型。在我的例子中,它是一个模板类型。这似乎是麻烦(或者是吗?)

所以我尝试了(online here):

template<typename U>
struct B  U valb; ;

template<typename U>
struct C  U valc; ;

// forward declaration of class A
namespace ns 
template<typename U,typename V> struct A;


// forward declaration of operator
template<typename T1,typename T2>
ns::A<T1,T2>
operator * ( const B<T2>&, const C<T1>& );

namespace ns 
  template<typename U,typename V>
  struct A                            // class declaration
  
    template<typename T1,typename T2>
    friend A<T1,T2> ::operator * ( const B<T2>&, const C<T1>& );

    private:
      U v1; V v2;
  ;
 // namespace 

// operator definition
template<typename T1,typename T2>
ns::A<T1,T2> operator * ( const B<T2>& b, const C<T1>& c )

    ns::A<T1,T2> a;
    a.v1 = c.valc * b.valb; // dummy
    return a;


int main()

    B<float> b;
    C<int> c;
    auto a = b * c;

这无法构建:

error: ISO C++ forbids declaration of 'operator*' with no type [-fpermissive]    
   23 |         friend A<T1,T2> ::operator * ( const B<T2>&, const C<T1>& );

如果我删除::,那么操作员就不会被识别为朋友。

我做错了什么?我该如何解决这个问题?

【问题讨论】:

【参考方案1】:

除非已经声明了befriend函数模板,否则我认为您不会在类定义之外定义此函数模板:本质上,没有办法实际命名操作符。更有趣的是,操作的两个参数类型实际上在不同的命名空间中,即operator*() 确实需要在与ns 不同的命名空间中定义。看来这可以解决问题:

template<typename U> struct B  U valb; ;
template<typename U> struct C  U valc; ;

// declaration of ns::A to declare the operator*
namespace ns  template<typename U,typename V> struct A; 

template<typename T1,typename T2>
ns::A<T1,T2> operator * ( const B<T2>&, const C<T1>& );

namespace ns 
    template<typename U,typename V>
    struct A 
        template<typename T1,typename T2>
        friend auto ::operator * ( const B<T2>&, const C<T1>& ) -> A<T1, T2>;

    private:
        U v1; V v2;
    ;


template<typename T1,typename T2>
ns::A<T1,T2> operator * ( const B<T2>& b, const C<T1>& c ) 
    ns::A<T1,T2> a;
    a.v1 = c.valc * b.valb; // dummy
    return a;


int main()

    B<float> b;
    C<int> c;
    auto a = b * c;

如需现场演示,请参阅Compiler Explorer link。

【讨论】:

感谢您的回答。但是...您的链接代码无法链接??? (undefined reference to ns::A&lt;int, float&gt; operator*...。我不明白为什么......)。我错过了什么吗?并且知道为什么全局限定在没有模板化返回类型的情况下有效并且在它存在时会失败? @kebs 你是对的。我实际上并没有注意到操作的两个参数位于不同的命名空间中。结果证明这是一个有趣的谜题,但我认为update Compiler Explorer link 显示了修复。我会尽快更新我的答案。 啊,好吧,我明白了,它的尾随返回类型可以解决问题!我不经常使用它,所以没有考虑尝试。谢谢!

以上是关于命名空间中的类并使用模板类型作为返回类型时的全局范围友元运算符声明的主要内容,如果未能解决你的问题,请参考以下文章

Boost.Python:匹配 C++ 模板类型的嵌套命名空间

获取命名空间内的所有类[重复]

在 QML 中使用 C++-slot,它返回命名空间中的类型

交友/使用不同命名空间中的类

包含命名空间的类模板的转发声明会导致编译错误

C ++良好的编码风格 - 始终完全限定库类型?