The following code doesn't work because the t member function can't access the attribute of its argument object.
How to declare template method t of template class A as a friend function of A?
For the code without template, there is no need to declare friend.
Code:
template <typename T>
class A{
protected:
T a;
public:
A(int i){
a = i;
}
template <typename T1>
void t(const A<T1> & Bb){
a = Bb.a;
}
};
int main(void){
A<int> Aa(5);
A<float> Bb(0);
Aa.t(Bb);
}
Compiler Error (icc test.cpp):
test.cpp(11): error #308: member "A<T>::a [with T=float]" (declared at line 4) is inaccessible
a = Bb.a;
^
detected during instantiation of "void A<T>::t(const A<T1> &) [with T=int, T1=float]" at line 17
Code without template:
class A{
protected:
int a;
public:
A(int i){
a = i;
}
void t(const A & Bb){
a = Bb.a;
}
};
int main(void){
A Aa(5);
A Bb(0);
Aa.t(Bb);
}
You can make all template instantiations friends of one another.
template <typename T>
class A {
protected:
// This makes A<int> friend of A<float> and A<float> friend of
// A<int>
template <typename T1> friend class A;
T a;
public:
A(int i){
a = i;
}
template <typename T1>
void t(const A<T1> & Bb){
a = Bb.a;
}
};
int main(void){
A<int> Aa(5);
A<float> Bb(0);
Aa.t(Bb);
}
A<T> and A<T1> are two different types, if T and T1 are two different types. You might as well replace A<T> with Foo and A<T1> with Bar in such a scenario. At that point it should be fairly obvious why you would need to make Foo and Bar friends (ehm, A<T> and A<T1> where T and T1 are not the same type).
Now, take a look at your error:
detected during instantiation of "void A<T>::t(const A<T1> &) [with T=int, T1=float]"
It's telling you it's calling your t() function on an object of type A<T>, passing in an object of type A<T1> as a parameter, where T=int and T1=float. This makes the object that's calling the function of a different class (A<int>) than the class of the object being used as the parameter (A<float>), and since they're different classes, they can't access each others' protected members without being friends.
Related
With a class defined as follows:
template <typename T>
class A {
private:
T a;
public:
A(T& a) : a_(a) { }
template <typename D>
void Eval(D& arg)
{
// ...
}
};
template A<int>;
I want to explicitly instantiate one instance of the class, and I want this class to have one explicit instantiation of Eval. The intention here is to get a member function pointer that avoids ambiguity:
auto eval_ptr = &A<int>::Eval;
The ambiguity is not coming from anything to do with template instantiation of the class, it's caused by Eval also being a templated function.
&A<int>::Eval does not point to a function, it points to a template. And there is just no such type as a "pointer to a template".
If you want a pointer to A<int>::Eval, you need to specify D as well.
auto eval_ptr = &A<int>::Eval<int>; works just fine for example.
Addendum: Pointers-to-templates do exist in the grammatical sense, but there is no type an object can have to hold one of them. They must be immediately casted/decayed to a specific overload in order to be used, which doesn't come into play here since you want to store it in an auto.
For example: The following is fine because there's clearly only one "version" of Eval that can be meant:
void bar(void (A<int>::*arg)(int&)) {}
void foo() {
bar(&A<int>::Eval);
}
The very simple solution was specifying both template parameters:
template <typename T>
class A
{
private:
T a;
public:
A(T &a) : a_(a) {}
template <typename D>
void Eval(D &arg)
{
arg+=1;
}
};
int main()
{
auto p = &A<int>::Eval<int>;
}
In other words, why does this compile fine :
template<typename Type>
class A{
public:
void f();
};
class B{
friend void A<int>::f();
};
template<>
void A<int>::f(){
B* var = new B();
}
While this doesn't :
template<typename Type>
class A{
public:
void f();
};
template<typename Type> // B is now a templated class
class B{
friend void A<Type>::f(); // Friending is done using B templated type
};
template<>
void A<int>::f(){
B<int>* var = new B<int>(); // var is now declared using int as its templated type
}
For the second code snippet, compiler (gcc 6.2, no special flags) says:
main.cpp: In instantiation of ‘class B<int>’:
main.cpp:14:28: required from here
main.cpp:9:15: error: prototype for ‘void A<int>::f()’ does not match any in class ‘A<int>’
friend void A<Type>::f();
^~~~~~~
main.cpp:13:6: error: candidate is: void A<Type>::f() [with Type = int]
void A<int>::f(){
As I understand it, in the second code snippet, when declaring var the compiler should parse B class declaration, replace the Type used in the friend declaration by int, and everything should work fine. What am I missing?
EDIT : comments below have pointed out that the second code snippet seems to compile correctly with clang and Visual C++ 2015
An explicit instantiation of B<int> before it is used in A<int>::f() resolves this problem. I assume GCC tries an implicit instantiation of B<int> in the definition of A<int>::f(). But the definition of A<int>::f() is not finished and GCC 'looses' the friend declaration. It looks like a compiler problem.
template<typename Type>
class A
{
public:
void f();
};
template<typename Type> // B is now a templated class
class B
{
friend void A<Type>::f(); // Friending is done using B templated type
};
template
class B<int>; // <= explicit instantiation, that works
template<>
void A<int>::f()
{
B<int>* var = new B<int>();
}
Specializing template class member function without specializing whole template class is special case when you are allowed to specialize non-template member function, so maybe GCC is confused, and I don't know the reasons, but somehow you can't declare friendship to specialized non-template member of template class. Temporary solution would be to specialize whole class template for this to work.
//class template A
template<typename Type>
class A{
public:
void f();
};
//class A<int>
template<>
class A<int>{
public:
void f();
};
Then, define A<int>::f:
For class B:
void A<int>::f(){
B* var = new B();
(void)(var);
}
For template class B:
void A<int>::f(){
B<int>* var = new B<int>();
(void)(var);
}
But I think Clang is right here, there should be no problems for such friend declaration. It's probably a bug in GCC.
.hpp
template <typename T>
struct A { virtual A& modify() = 0; };
template <typename T>
struct B : virtual A<T> {};
template <typename T>
struct C : B<T> { C& modify() final; };
.cpp
template <typename T>
C<T>& C<T>::modify() {
// …
return *this;
}
// explicit instantiation
template struct C<double>;
I need some methods to return references in order to make “chains” / define an assignment operator / etc.:
C<double> a, b, c;
// …
a = (b = c).modify();
I also have to deal with virtual inheritance to avoid “the diamond problem” (omitted here for simplicity).
However, this does not work:
MSVC:
Error C2908: explicit specialization;
'A<T> &C<T>::modify(void)' has already been instantiated
Explicit instantiation works fine w/o virtual inheritance. So I am wondering what is wrong here.
(Also everything works fine if there are no member functions returning object references / pointers.)
The correct syntax for explicit instantiation should be:
template struct C<double>;
^^^^^
also, you still need to specify type parameter for your C template:
C<double> a, b, c;
^^^^^^
at least g++ and clang accepts this code: http://coliru.stacked-crooked.com/a/23ba6a238a7a17da
but Visual Studio does not...
looks like VS does not like covariant return types, the following compiles under g++/clang and VS but - no covariant return in modified() : http://coliru.stacked-crooked.com/a/70c8e64f0824129a
The following code does not compile and I don't know why:
#include <type_traits>
// Base class definition
template<template<typename> class CRTP, typename T> class Base
{
// Friend function declaration
public:
template<template<typename> class CRTP0, typename T0, class>
friend int func(const Base<CRTP0, T0>& rhs);
// Protected test variable
protected:
int n;
};
// Friend function definition
template<template<typename> class CRTP0, typename T0,
class = typename std::enable_if<true>::type>
int func(const Base<CRTP0, T0>& rhs)
{
return rhs.n;
}
// Derived class definition
template<typename T> class Derived : public Base<Derived, T> {};
// Main
int main()
{
Derived<int> x;
func(x);
return 0;
}
GCC 4.6.2 (and GCC 4.7.1 on LWS) tells me that :
error: 'int Base<Derived, int>::n' is protected
Meaning that basically, the friendship is not correctly detected. As this is just an extract of my code, I would like to put the definition of the friend function outside of the class definition, like here. So, what is the problem and how to solve it ?
EDIT : I've modified the code to try to isolate the problem and make it far more readable. The current enable_if is always true but for my real code, I will have a "real" condition, here it is just to isolate the problem.
EDIT2 : The liveworkspace is here : friend function problem
I had similar problems with gcc. I think this is a compiler bug: Because your function template has three template parameters rather than the two in the friend declaration, it doesn't match them. In order for your intentions to work correctly with gcc, you must match the friend declaration exactly. This is most easily achieved by using the SFINAE on the function return type
// Friend function definition
template<template<typename> class CRTP0, typename T0>
typename std::enable_if<true,int>::type
func(const Base<CRTP0, T0>& rhs)
{
return rhs.n;
}
If you try to call func explicitly:
func<Derived, int, void>(x);
g++ complains with:
source.cpp:31:31: error: call of overloaded 'func(Derived<int>&)' is ambiguous
source.cpp:31:31: note: candidates are:
source.cpp:19:5: note: int func(const Base<CRTP0, T0>&) [with CRTP0 = Derived; T0 = int; <template-parameter-1-3> = void]
source.cpp:9:20: note: int func(const Base<CRTP0, T0>&) [with CRTP0 = Derived; T0 = int; <template-parameter-2-3> = void; CRTP = Derived; T = int]
I believe the problem is that the friend declaration is not being correctly identified with the function definition; instead another function template is being declared. This happens even if the class and function templates are predeclared.
template <typename T> class Foo;
template <typename T> int g(Foo<T> const&);
template <typename T> class Foo
{
public:
template <typename U> int f(Foo<U> const& p) const { return p.m; }
// which friend declaration will allow the above function to compile? The
// next one doesn't work.
template <typename U> friend void Foo<U>::template f<T>(Foo<T> const&) const;
// while this one work for g().
friend int g<T>(Foo<T> const&);
private:
int m;
};
template <typename T> int g(Foo<T> const& p) { return p.m; }
// Let's call them
void bar()
{
Foo<int> fi;
Foo<double> fd;
fd.f(fi);
g(fi);
}
The above doesn't compile with g++ nor Como. g() is here to show what I would like to do with f().
For instance, here are g++ messages:
foo.cpp:11: error: invalid use of template-id ‘f<T>’ in declaration of primary template
foo.cpp: In member function ‘int Foo<T>::f(const Foo<U>&) const [with U = int, T = double]’:
foo.cpp:27: instantiated from here
foo.cpp:17: error: ‘int Foo<int>::m’ is private
foo.cpp:7: error: within this context
and como's one:
"ComeauTest.c", line 11: error: an explicit template argument list is not allowed on
this declaration
template <typename U> friend void Foo<U>::template f<T>(Foo<T> const&) const;
^
"ComeauTest.c", line 7: error: member "Foo<T>::m [with T=int]" (declared at line 17)
is inaccessible
template <typename U> int f(Foo<U> const& p) const { return p.m; }
^
detected during instantiation of "int Foo<T>::f(const Foo<U> &)
const [with T=double, U=int]" at line 27
2 errors detected in the compilation of "ComeauTest.c".
Variants suggested by the error messages didn't either.
BTW, I know of the obvious work around
template <typename U> friend class Foo<U>;
Edit:
14.5.4/5 (of n3225, 14.5.3/6 of C++98 is similar but the following text is clearer in n3225) starts by
A member of a class template may be declared friend of a non-template class...
which could imply that a member of a class template may not be declared friend of a template class but my first interpretation would that this sentence was an introduction for the following explanations (mainly they apply to any specialisation, explicit or not, given that the prototype is correct).
I think you have to say template <> before the friend declaration so it knows you're friending a specialization.
EDIT: I can't get any error with your code, even making an instantiation and calling g. Can you post a minimal set of actual code that's causing the error, along with the error message?
Though I'm not 100% sure this is strictly standard conforming,
the following code can be compiled by ideone(gcc-4.3.4) and Comeau online:
template< class >
class Foo {
int m;
public:
template< class U > int f( Foo<U> const& p ) const { return p.m; }
template< class U > template< class V >
friend int Foo<U>::f( Foo<V> const& ) const;
};
void bar() {
Foo<int> fi;
Foo<double> fd;
fd.f( fi );
}
Hope this helps.