问题
I would like to know if what I am aiming for is possible.
I have a class Class such that
#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;
}
But this way, f can access private/protected members of instances of Class where Class's type is not the same as f's arguments' type, like in the line
std::cout << R.m_t << std::endl;
(R is of type W, not T)
My question is this: is there a way I can define f as a friend function that has a template parameter specifying the return type (W), but can only access private/protected members of Class objects that are the same type as its arguments' type?
Edit 1: The solution submitted by @cantordust, while clean and aesthetic, doesn't work when Class and f are in a namespace, alas making it unsuitable for more general usecases.
For example, if in a modification of cantordust's code namespace n
starts just after the include declaration, and ends just before the main function, there will be no other way to use f
than to put using n::f;
in main, which, together with its implications, is inexcusable of well-written C++ code.
Edit 2: There is yet another solution: defining a member function, and optionally defining a similar regular function with the same parameters, and calling the member function from it. The code would look something like this:
// 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 */ }
The procedure for defining the regular function is obvious.
回答1:
You could indirect through a template class
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.
The indirection is necessary since you can't befriend a partial specialization.
回答2:
Under mild assumptions, you don't need a helper struct:
#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
来源:https://stackoverflow.com/questions/51556613/restrict-the-scope-of-class-instances-accessible-by-multiple-template-parameter