【问题标题】:Declare a template function taking two objects of a template class friend to (only) those two specializations声明一个模板函数,将模板类友元的两个对象(仅)用于这两个特化
【发布时间】:2018-03-24 16:38:59
【问题描述】:

我有一个模板类Test(以整数作为模板参数)和一个模板函数(在本例中为operator*),它接受Test 类的两个对象,模板参数可能不同。该函数需要对它的两个参数都友好。这是一个最低限度的工作示例:

#include <type_traits>

template <int N>
class Test;

template <int N1, int N2>
Test<N1+N2> operator* (Test<N1>, Test<N2>);

template <int N>
class Test {
  double val;
public:
  Test (double x) : val{x} {}
  template <int N1, int N2>
  friend Test<N1+N2> operator* (Test<N1>, Test<N2>);
};

template <int N1, int N2>
Test<N1+N2> operator* (Test<N1> x, Test<N2> y) {
  return Test<N1+N2> {x.val*y.val};
}

int main (int argc, char* argv[]) {
  Test<1> a{4.}, c{7.9};
  Test<2> b{3.5};
  a*b;
  a*c;
  return 0;
}

这行得通,但该函数是Test 的每个专业的朋友。我只想和Test&lt;N1&gt;Test&lt;N2&gt; 成为朋友。

我尝试这样声明:

template <int N>
class Test {
  double val;
public:
  Test (double x) : val{x} {}
  template <int N1, int N2>
  friend std::enable_if_t<N1==N||N2==N,Test<N1+N2>> operator* (Test<N1>, Test<N2>);
};

但遇到不明确重载的 g++ 错误。我也试过了:

template <int N>
class Test {
  double val;
public:
  Test (double x) : val{x} {}
  template <int N1, int N2, typename = std::enable_if_t<N1==N||N2==N>>
  friend Test<N1+N2> operator* (Test<N1>, Test<N2>);
};

但友元声明不允许使用默认模板参数。

我更喜欢 C++14 中的解决方案,但 C++17 中的解决方案也可以接受。

更新:遵循 S.M. 的回答。我建议有类似问题的人使用以下解决方案

template <int N>
class Test {
  double val;
public:
  Test (double x) : val{x} {}

  template <int N2>
  Test<N+N2> operator* (Test<N2> y) {
    return Test<N+N2> {val*y.val};
  }

  template <int N2>
  friend class Test;
};

int main (int argc, char* argv[]) {
  Test<1> a{4.}, c{7.9};
  Test<2> b{3.5};
  a*b;
  a*c;
  return 0;
}

【问题讨论】:

  • 这是一个奇怪的要求。所以你希望那些不满足条件的函数存在但不能访问私有数据?一个合理的解决方案是首先禁用它们。
  • 我无法理解这个要求。如果不调用 operator*,您希望它不是朋友吗?恕我直言,模棱两可是可以的。一方面是删除的运算符声明,另一方面是全局声明。编译器无法选择使用哪一个。
  • 为什么operator*&lt;5, 3&gt; 首先会修改Test&lt;6&gt; 的私有值?为什么你需要保护它?
  • @liliscent 要了解为什么需要此要求,请在此处查看方法 #1 的问题web.mst.edu/~nmjxv3/articles/templates.html
  • @S.M.请看上面的评论

标签: c++ templates c++14 friend-function


【解决方案1】:

我提供以下解决方案。该函数不再是友元函数。

#include <type_traits>

template <int N>
class Test {
  double val;
public:
  Test (double x) : val{x} {}

  template <int N2>
  Test<N+N2> mul(const Test<N2> &y) const {
    return Test<N+N2>{val * y.val};
  }

  template <int N2>
  friend class Test;
};

template <int N1, int N2>
Test<N1+N2> operator* (const Test<N1> &x, const Test<N2> &y) {
  return Test<N1+N2>{x.template mul<N2>(y)};
}

int main (int argc, char* argv[]) {
  Test<1> a{4.}, c{7.9};
  Test<2> b{3.5};
  a*b;
  a*c;
  return 0;
}

作为会员operator *:

#include <type_traits>

template <int N>
class Test {
  double val;
public:
  Test (double x) : val{x} {}

  template <int N2>
  Test<N+N2> operator *(const Test<N2> &y) const {
    return Test<N+N2>{val * y.val};
  }

  template <int N2>
  friend class Test;
};

int main (int argc, char* argv[]) {
  Test<1> a{4.}, c{7.9};
  Test<2> b{3.5};
  a*b;
  a*c;
  return 0;
}

【讨论】:

  • 非常感谢。您能否提供一个参考来解释在x.template mul&lt;N2&gt;(y) 中使用template 关键字。如果我们将 Test 的所有特化彼此友好(这是合理的),我们也可以将乘法运算符定义为成员而不是非成员,对吧?
  • 是的,我们可以将操作符定义为成员函数。我不是您的回答中的成员。看看我的更新。
  • 关于使用template关键字的问题的答案看这里stackoverflow.com/questions/610245/…
【解决方案2】:

不,你不能这样做。

17.5.4 朋友 [temp.friend]

类或类模板的朋友可以是函数模板或类模板,函数模板或类模板的特化,或非模板函数或类。

在这 6 个选项中,您可能会对函数模板感兴趣。

所以你可以通过三种方式与操作员交朋友

template <int N1, int N2>
friend Test<N1+N2> operator* (Test<N1>, Test<N2>); #1

template <int N2>
friend Test<N + N2> operator* (Test<N>, Test<N2>); #2

template <int N1>
friend Test<N1 + N> operator* (Test<N1>, Test<N>); #3

您尝试过的选项 #1 并没有按照您的意愿工作。选项 #2 和 #3 也不起作用,原因有两个:首先,您需要在某个地方定义此重载,其次,如果您定义这样的重载,它将不适用于第二个参数 (#2) 和第一个参数 ( #3)。

【讨论】:

  • #2 和 #3 不声明专业化。它们为每个可能的N 声明了不同的重载,并且您需要以某种方式将它们全部定义。
  • @aschepler Yeh,你说得对,这些都是重载。谢谢!
猜你喜欢
  • 2016-02-24
  • 1970-01-01
  • 2013-09-18
  • 2018-02-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-01
相关资源
最近更新 更多