【问题标题】:How to declare template member function of template class X as friend of nested class X::Y如何将模板类 X 的模板成员函数声明为嵌套类 X::Y 的朋友
【发布时间】:2019-01-29 09:20:10
【问题描述】:

我有一个模板类。它具有模板功能。两者都采用不同的模板参数。有一个内部类需要与封闭类的模板函数交朋友。编译器错误比比皆是。以下玩具示例显示了我的问题。

首先,以下当然是编译(VS 2017):

template <typename T>
class Class1
{
    public:
        Class1() = default;
        ~Class1() = default;

        template <typename U>
        void Func(U& x) {};
};

class Class2
{
    public:
        Class2() = default;
        ~Class2() = default;

        template <typename T>
        template <typename U>
        friend void Class1<T>::Func(U& x);
};

int main()
{
    Class1<int> c1;
    return 0;
}

现在让我们将Class2 移动到Class1,不做其他更改:

template <typename T>
class Class1
{
    public:
        Class1() = default;
        ~Class1() = default;

        template <typename U>
        void Func(U& x){};

    class Class2
    {
        public:
            Class2() = default;
            ~Class2() = default;

            template <typename T> //Compiler error here.
            template <typename U>
            friend void Class1::Func(U& x);
    };
};

int main()
{
    Class1<int> c1;
    return 0;
}

现在我得到一个编译器错误:error C3856: 'Class1&lt;T&gt;::Func': class is not a class template

当类嵌套时,我尝试了各种方法来声明朋友,但我无法编译它。可能没有办法做我想做的事。

请注意,我正在尝试做的事情的语义(在实际代码中,而不是这个玩具示例中)是这样的,即 Func 应该是一个成员函数。这与迭代器或运算符无关,它们当然通常是非成员函数。我在这里看到了一些类似的问题,但它们通常与迭代器或运算符有关,我还没有找到适合我的解决方案的问题。

更糟的是,考虑到我的设计,将所有Class1 声明为Class2 的朋友是可以的(这样做可以让我解决这个问题)。 Class2 是一个与Class1 完全耦合的小助手类;它的所有特殊成员,除了析构函数和移动ctor,要么是私有的,要么是已删除的,Class1::Func 是唯一实例化Class2 的东西(并通过move ctor 将其返回给Class1 的用户)。因此,虽然与整个 Class1 成为朋友并不理想,但在紧要关头还是可以的。

提前致谢。

【问题讨论】:

  • 我刚刚认出template &lt;typename U&gt; void Class1&lt;T&gt;::Func(U&amp; x) {}public。所以,只需删除 friend 的东西。 (我在开玩笑。)如果是private,你的 MCVE 会更有意义。我认为这是实际意图。
  • @scheff Ha,是的,这只是一个玩具示例,因此某些类成员的访问级别与真实代码中的不同。

标签: c++ templates inner-classes friend-function


【解决方案1】:

这似乎是 MSVS 编译器中的一个错误。 gcc 和 clang 都接受了带有 Scheff 更正的代码。作为临时解决方法,可以使用#ifdef

#ifdef _MSC_VER
    friend class Class1;
#else
    template <typename U>
    friend void Class1::Func(U& x);
#endif

加法。Bug report

【讨论】:

  • 感谢您确认这是一个错误。
【解决方案2】:

我首先尝试在 wandbox.org 上使用 gcc HEAD 9.0.0 重现 OP 的问题并得到:

Start

prog.cc:17:23: error: declaration of template parameter 'T' shadows template parameter
17 |             template <typename T> //Compiler error here.
   |                       ^~~~~~~~
prog.cc:1:11: note: template parameter 'T' declared here
1 | template <typename T>
  |           ^~~~~~~~
prog.cc: In function 'int main()':
prog.cc:25:17: warning: unused variable 'c1' [-Wunused-variable]
25 |     Class1<int> c1;
   |                 ^~

1

Finish

修复很简单——T 已经是一个模板参数,必须在嵌套的friend 声明中重命名:

template <typename T>
class Class1
{
    public:
        Class1() = default;
        ~Class1() = default;

        template <typename U>
        void Func(U& x){};

    class Class2
    {
        public:
            Class2() = default;
            ~Class2() = default;

            template <typename T1> //Compiler error gone.
            template <typename U>
            friend void Class1<T1>::Func(U& x);
    };
};

int main()
{
    Class1<int> c1;
    return 0;
}

再次在 wandbox.org 上测试:

Start

prog.cc: In function 'int main()':
prog.cc:25:17: warning: unused variable 'c1' [-Wunused-variable]
25 |     Class1<int> c1;
   |                 ^~

0

Finish

如果打算 friend void Class1::Func(U&amp; x); 依赖于相同的模板参数 T 就像 Class1 这将是替代解决方案:

template <typename T>
class Class1
{
    public:
        Class1() = default;
        ~Class1() = default;

        template <typename U>
        void Func(U& x){};

    class Class2
    {
        public:
            Class2() = default;
            ~Class2() = default;

            template <typename U>
            friend void Class1::Func(U& x);
    };
};

int main()
{
    Class1<int> c1;
    return 0;
}

再次在 wandbox.org 上测试:

Start

prog.cc: In function 'int main()':
prog.cc:24:17: warning: unused variable 'c1' [-Wunused-variable]
24 |     Class1<int> c1;
   |                 ^~

0

Finish

【讨论】:

  • 这似乎是一个 MSVS 错误,OP 的代码(在 friend 声明中删除了 template&lt;typename T&gt;)无法编译。
  • @Evgeny 你在 VS2017 中试过了吗? (我只使用 g++ 并按预期工作。)我在 OP 的问题中错过了这一点,并假设他/她没有检查(第二个工作变体)。
  • 我在最新的测试版VS2018中试过了。在您的第一个区块中,您可能在friend void Class1&lt;T1&gt;::Func(U&amp; x); 中错过了&lt;T1&gt;
  • @Evgeny AFAIK,如果模板参数被忽略,那么(Class1 的定义内部)Class1 === Class1&lt;T&gt;。我不得不承认这与template &lt;typename T1&gt; 一起没有多大意义...
  • @scheff 感谢您和大家 - 我曾尝试将模板参数更改为不同的东西(以及尝试其他一些东西),但它仍然没有奏效。这似乎是由于VS中的一个错误。遗憾的是,这段代码的真实版本通常是用 gcc 编译的,但我把我的 gcc 电脑放在工作中,在家里乱搞代码。所以如果我不是健忘的话,我什至不必写这篇文章。再次感谢。
猜你喜欢
  • 2019-02-06
  • 1970-01-01
  • 1970-01-01
  • 2017-02-09
  • 1970-01-01
  • 2019-09-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多