【问题标题】:Restrict the scope of class instances accessible by multiple template parameter friend function限制多个模板参数友元函数可访问的类实例的范围
【发布时间】:2019-01-04 11:58:54
【问题描述】:

我想知道我的目标是否可行。

我有一个类这样的类

#include<iostream>

template<class T> class Class;
template<class T, class W> Class<W> f(Class<T>& C, const Class<T>& D);

template<class T> class Class {
protected: // this could be private
    T m_t;
public:
    Class(): m_t(T()) {}
    Class(T t): m_t(t) {}
    T& getT() { return m_t; }
    template<class U, class W> friend Class<W> f(Class<U>& C, const Class<U>& D);
};

template<class T, class W> Class<W> f(Class<T>& C, const Class<T>& D)
{
    C.m_t += D.m_t;
    Class<W> R;
    std::cout << R.m_t << std::endl; // I don't want this to be possible
    return R;
}

int main()
{
    Class<int> C(42), D(24);
    std::cout << f<int, char>(C, D).getT() << std::endl;
}

但是通过这种方式,f 可以访问 Class 实例的私有/受保护成员,其中 Class 的类型与 f 的参数类型不同,就像在行中一样

std::cout << R.m_t << std::endl;

(R是W类型,不是T)

我的问题是:有没有一种方法可以将 f 定义为友元函数,该函数具有指定返回类型 (W) 的模板参数,但只能访问与以下类型相同的 Class 对象的私有/受保护成员它的参数类型?

编辑 1:@cantordust 提交的解决方案虽然干净且美观,但当 Class 和 f 位于命名空间中时不起作用,唉,它不适合更一般的用例。 例如,如果在对 cantordust 代码的修改中,namespace n 在 include 声明之后开始,并在 main 函数之前结束,那么除了将 using n::f; 放入 main 之外,没有其他方法可以使用f,其中,连同它的含义,对于编写良好的 C++ 代码是不可原谅的。

编辑2:还有另一种解决方案:定义一个成员函数,并可选地定义一个具有相同参数的类似常规函数,并从中调用成员函数。 代码看起来像这样:

// inside Class
template<class W> Class<W> f(Class& C, Class& D);
//outside Class
template<class T> template<class W> Class<W> Class<T>::f(Class<T>& C, Class<T>& D)
{ /* definition */ }

定义正则函数的过程是显而易见的。

【问题讨论】:

  • 你可以为函数模板的特化而不是整个模板函数加好友
  • 对。但这就是问题所在。如果我这样做 template&lt;class W&gt; friend Class&lt;W&gt; f&lt;T, W&gt;(Class&lt;T&gt;&amp; C, const Class&lt;T&gt;&amp; D) 我会得到错误错误:在主模板声明中无效使用模板 ID ‘f&lt;T, W&gt;’ template&lt;class W&gt; friend Class&lt;W&gt; f&lt;T, W&gt;(Class&lt;T&gt;&amp; C, const Class&lt;T&gt;&amp; D);
  • 模板函数不能部分特化。我认为错误可能来自那里
  • 是的,我也是这么想的。但我还能如何专攻 f?
  • 你可以将你的函数包装成一个部分专用的结构

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


【解决方案1】:

你可以通过模板类间接

template<class T> class Class;
template<typename>
struct fs;

template<class T> class Class {
protected: // this could be private
    T m_t;
public:
    Class(): m_t(T()) {}
    Class(T t): m_t(t) {}
    T& getT() { return m_t; }
    friend struct fs<T>;
};

template<typename T>
struct fs
{
    template<typename W>
    static Class<W> f(Class<T>& C, const Class<T>& D)
    {
        C.m_t += D.m_t;
        Class<W> R;
        std::cout << R.m_t << std::endl; // ill-formed
        return R;
    }
};

template<class T, class W>
Class<W> f(Class<T>& C, const Class<T>& D)
{
    return fs<T>::template f<W>(C, D);
}

Live.

间接是必要的,因为您无法与部分专业化成为朋友。

【讨论】:

  • 不得不盯着看一点来处理,但我现在已经接近理解了。这确实解决了我的问题,没有任何“漏洞”。非常感谢!
【解决方案2】:

在温和的假设下,您不需要辅助结构:

#include<iostream>

template<class T> class Class;
template<typename U, typename W>
Class<W> f(Class<U>& C, const Class<U>& D);

template<class T>
class Class
{
protected: // this could be private

    T m_t;

public:
    Class()
        :
          m_t(T())
    {}

    Class(T t)
        :
          m_t(t)
    {}

    T& getT()
    {
        return m_t;
    }

    template<typename U, typename W>
    friend Class<W> f(Class<T>& C, const Class<T>& D)
    {
        C.m_t += D.m_t;
        Class<W> R;
        std::cout << R.m_t << std::endl; // I don't want this to be possible
        return R;
    }
};

int main()
{
    Class<int> C(42), D(24);
    std::cout << f<int, char>(C, D).getT() << std::endl;
}

Demo here

【讨论】:

  • 相当优雅的解决方案,谢谢!事实证明,我对 C++ 的了解比我以前想象的要少,但我想这就是学习的方式。
  • :) 我也一直有这种感觉。很高兴能帮上忙!
  • 我刚刚注意到您升级了解决方案。实际上,我是按照您之前的回答给出的直觉想到了这个想法,将代码复制到我的 IDE 中并意识到它已经完成了。有那么一瞬间,我以为那是某种邪恶的魔法。无论如何,我想我在这里学到了一个重要的教训:不要满足于少。再次感谢!
  • don't settle for less - 我想我会采纳这个作为我的座右铭! :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-19
  • 1970-01-01
  • 2011-07-15
相关资源
最近更新 更多