To be consistent with other (non-template) functions in a class I wanted to define and invoke a friend template function.
I can define it with no problem (see function t below).
namespace ns{
struct S{
void m() const{}
friend void f(S const&){}
template<class T>
friend void t(S const&){}
};
template<class T>
void t2(S const& s){}
}
However later I am not able to invoke this t function in any way?
int main(){
ns::S s;
s.m();
f(s);
// t<int>(s); // error: ‘t’ was not declared in this scope (I was expecting this to work)
// ns::t<int>(s); // error: ‘t’ is not a member of ‘ns’
// ns::S::t<int>(s); // error: ‘t’ is not a member of ‘ns::S’
}
Even if it is not possible at all, I am surprised that I am allowed to define it.
I tested this with gcc 8 and clang 7.
What you need for this to work are a couple of forward declarations.
The below two lines of code should come before the namespace ns.
struct S; //Needed because S is used as a parameter in the function template
template<class T> void t(S const&);
And then this form of call will work inside main.
t<int>(s);
See demo here.
Related
Following compiles perfectly fine:
struct MyClass {
template<typename SameName>
void foo (SameName* p);
};
struct SameName {};
template<class SameName>
void MyClass::foo (SameName* p) {}
However, if we enclose MyClass and SameName inside some class Outer then the template function defined outside, fails to compile.
struct Outer {
/* paste here `MyClass` & `SameName` from above */
};
template<class SameName>
void Outer::MyClass::foo (SameName* p) {} // <--- error here
// ^^^^^
The g++ (03-14) error is weird:
error: prototype for ‘void Outer::MyClass::foo(Outer::SameName*)’ does not match any in class ‘Outer::MyClass’
void Outer::MyClass::foo (SameName* p) {}
^~~~~
templateClassMethod.cpp:6:10: error: candidate is: template<class SameName> void Outer::MyClass::foo(SameName*)
void foo (SameName* p);
The clang (03-14) error is less intuitive:
error: out-of-line definition of 'foo' does not match any declaration in 'Outer::MyClass'
void Outer::MyClass::foo (SameName* p) {}
Question:
Is this a language/compiler bug or an expected behaviour?
If expected, then why is the choice for template type's name being restricted for inner classes?
[Note: Actually I had many template parameters, and incidentally one of them matched the inner class name, by chance. It took me 1 hour to figure out. The error generated in that complex scenario was totally misleading.]
Is this code invalid:
template <class T> struct A;
class C {
template <class T> friend void A<T>::foo();
};
In GCC 6.1.0 it says:
error: member 'void A<T>::foo()' declared as friend before type 'A<T>' defined
template <class T> friend void A<T>::foo();
Clang 3.8.0:
warning: dependent nested name specifier 'A<T>::' for friend class declaration
is not supported; turning off access control for 'C' [-Wunsupported-friend]
And Visual Studio 2015 crashes:
fatal error C1001: An internal error has occurred in the compiler.
(compiler file 'f:\dd\vctools\compiler\cxxfe\sl\p1\c\template.cpp', line 8952)
template <class T> friend void A<T>::foo();
More specifically, is A required to be defined before the friend declaration?
template <class T> struct A;
class C {
static void foo();
template <class T> friend void A<T>::f();
};
template <class T> struct A {
void f() { }
};
If so, why?
You refer to A's member function foo. This function is not known to exist yet, because you only forward declare A.
In other words, you'll have to declare A<T>::foo before C, as the GCC message tries to tell you (the other two are rather cryptic). This means you have to declare the complete interface of A before C, instead of only forward declaring it.
Solution
You can declare f as a friend of C in the following way:
class C;
template <class T> struct A {
void f(C const &c);
};
class C {
int i = 42;
public:
static void foo();
template <class T> friend void A<T>::f(C const &c);
};
template <class T> void A<T>::f(C const &c) { std::cout << c.i << std::endl; }
Live Demo
Justification
According to the standard §3.3.2/p6 Point of declaration [basic.scope.pdecl]:
After the point of declaration of a class member, the member name can
be looked up in the scope of its class. [ Note: this is true even if
the class is an incomplete class. For example,
struct X {
enum E { z = 16 };
int b[X::z]; // OK
};
— end note ]
The diagnostic is rather misleading both for GCC and CLANG. The real problem with your code is not the fact that you're trying to access the definition of an incomplete type, but rather is that you can only refer to a class member name only after it has been declared.
The first problem is (I think) from §9.3/7 [class.mfct] and probably some other places in the standard (see clang message below and 101010's answer):
Previously declared member functions may be mentioned in friend declarations.
This problem is similar to the one from this question.
Since you did not declare A<T>::f before C, you cannot declare it has a friend of C.
But there is an hidden problem behing clang's message, if you make A a non-templated class, the message is different:
Incomplete type A in nested name specifier.
Which is closer to gcc message than the actual one, this is because clang's warning is about something else. Per §14.5.4/5 [temp.friend], the standard allows member of class template to be friend, so this must be valid:
template <typename T>
struct A {
void f ();
};
class C {
template <typename T>
friend void A<T>::f(); // Ok, A<T>::f is already declared
void private_member (); // See below
};
But clang still complains:
warning: dependent nested name specifier 'A::' for friend class declaration
is not supported; turning off access control for 'C' [-Wunsupported-friend]
clang does not support such friend declaration, so it simply turns off access control for C, meaning that:
C c;
c.private_member();
Will be "valid" everywhere, which may not be what you want.
Firstly, Forward declaration of a class is not sufficient if you need to use the actual class type, for example, if you need to use it as a base class, or if you need to use the methods of the class in a method.
Since here you try to use its details "foo()", there is no way compiler knows what is A::foo().. Compiler cannot distinguish if it is a typo (or) actual function.. it needs to know the declaration of A::foo() before you can use it.
If you still want to only to forward declare the class and make it a friend, see if friend classes fit your situation
template <class T> struct A;
class C{
template <class T> friend struct A;
};
I am writing a template class with a inner struct(public class). Now I have problems while defining the member function of the inner struct.
In the header file,
template <typename T>
class TMatrix {
...
struct Triplet {
void nop() const;
};
...
};
To define the function nop, in another cpp file I wrote
template <typename T>
void TMatrix<T>::Triplet::nop() {...}
// or
// void typename TMatrix<T>::Triplet::nop() {...}
But both of the two form don't compile. g++ says
error: expected unqualified-id before ‘)’ token
How can I correctly define this function separately?
Everything is working fine except you have to add const in function definition
template <typename T>
void TMatrix<T>::Triplet::nop() const {} // <--const is added in function declaration
You can check working example here
http://ideone.com/Yeio3w
I have a templated class within which I define free functions taking references to that templated class. These free functions are also templated on a different parameter.
From outside the class I can call the free functions. However, I cannot find the correct syntax for one free function to call another.
Quick example:
template<typename T> class Foo {
template<typename S>
friend S f(const Foo &) { return S(); }
template<typename S>
friend S g(const Foo &s) {
return f(s); // See below, when instantiated, yields 'no matching function for call to f(const Foo &)'
}
};
float test1() {
Foo<int> o;
return f<float>(o); // Compiles
}
float test2() {
Foo<int> o;
return g<float>(o); // Fails to compile as line above errors
}
(c.f. this link too)
It seems by the point of the call to f(s) within g(), the outermost template has been lost. How might I re-specify the T in the call to f? I have checked on GCC4.7, 4.8, clang 3.2 all with equivalent errors.
When you call f(s) you need to specify the template parameter S because it can't be deduced from the argument s.
But if you change it to f<S>(s) (assuming you meant to call it with the same template argument S as g was called with) then you inhibit ADL, and the only way a friend function defined at class scope can be found is by ADL. So you need to add a declaration of f to the global namespace, so that the call in g can find it.
So to make it work you need to add these declarations before Foo
template<typename T> class Foo;
template<typename S, typename T>
S f(const Foo<T> &);
template<typename S, typename T>
S g(const Foo<T> &);
and change the call in g to be f<S>(s) or something else like f<x>(s)
I'm trying to create a template class with a friend function which is inside a nested namespace. It works fine if I remove all the namespaces or if I remove all the templatization. But with both in place it won't compile. Let's look at some code:
namespace MyNamespace
{
// Forward declaration
template <typename Type>
class Container;
// Forward declaration
namespace AccessPrivateImplementation
{
template <typename Type>
Type getValue(Container<Type>* container);
}
// Templatized class
template <typename Type>
class Container
{
friend Type AccessPrivateImplementation::getValue(Container<Type>* volume);
private:
Type value;
};
// Friend function inside a namespace
namespace AccessPrivateImplementation
{
template <typename Type>
Type getValue(Container<Type>* container)
{
return container->value;
}
}
}
int main(int argc, char* argv[])
{
MyNamespace::Container<int> cont;
MyNamespace::AccessPrivateImplementation::getValue(&cont);
return 0;
}
The compiler (VS2010) tells me:
error C2248: 'MyNamespace::Container::value' : cannot access private member declared in class 'MyNamespace::Container'
Does anyone have any idea what I'm missing?
The friend declaration inside the Container class template declares a friend non-template function getValue() that lives in the AccessPrivateImplementation namespace.
However, you haven't provided such a function. Instead, what you have in the AccessPrivateImplementation namespace is a function template, whose appropriate specialization you want to be friend of Container<T> (for a given T).
To achieve this, the declaration you need is:
friend Type AccessPrivateImplementation::getValue<>(Container<Type>* volume);
// ^^
Here is a live example that shows your code compiling with the above fix.
As per my comment, if you declare the friend like so it will work:
friend Type AccessPrivateImplementation::getValue<>(Container<Type>* volume);
^^