This has been driving me crazy for the past couple hours, and I cannot seem to get around this problem. I have distilled the problem down to these 60 lines of code (including a main function).
#include <iostream>
namespace n1 {
// the general definition
template <class X, class Y> void f(X&, const Y&)
{
std::cout << "general template definition.\n";
}
} // namespace n1
namespace n2 {
// CRTP
template <class Derived> class A
{
int data;
// partial function template specialization for n1::f, and declare
// it a friend too, so that it may access the data attribute of A
template <class Y> friend void n1::f(A<Derived>& a, const Y& y);
}; // class A
} // namespace n2
namespace n1 {
// implementation for this particular function template specialization
template <class Derived, class Y> void f(n2::A<Derived>& a, const Y& y)
{
std::cout << "partial template specialization: " << a.data << "\n";
}
} // namespace n1
namespace n2 {
// Another class!
class B : public A<B>
{
}; // class B
} // namespace n2
namespace n1 {
// --------------------
// tricky part is here!
// --------------------
template <class Y> void f(n2::B& b, const Y& y)
{
// FAIL! not a friend! How?
f(static_cast<n2::A<n2::B>&>(b), y);
}
} // namespace n1
int main()
{
n2::B b;
int x;
n1::f(b, x); // should print "partial template specialization"
return 0;
}
So, what I "want" is to have the compiler select my function template specialization of n1::f whenever it is invoked with a concrete subclass of A<Derived>. In order to make sure that the compiler favors my specialization, I need to supply, for every subclass (B in this case), also a template specialization for n1::f that simply delegates the call. When that happens, I expect the data member variable of A<Derived> to be accessible to n1::f, because I declare n1::f to be a friend of A<Derived>. However, GCC complains that A<Derived>::data is private and inaccessible, see this snippet on Coliru.
Is this construction possible? If so, how can I get around the compiler complaining that A<Derived>::data is not accessible? (Making it public is not an option).
Your class definition must look like this:
template <class Derived> class A
{
int data;
template <class D, class Y> friend void n1::f(A<D>& a, const Y& y);
};
In fact, function declaration is:
template <class Derived, class Y> void f(n2::A<Derived>& a, const Y& y)
While your friend declaration is:
template <class Y> friend void n1::f(A<Derived>& a, const Y& y);
In this case, they are different beasts and that's why you receive that error. As you can see, template parameters lists are different. That's not a declaration of a function with a separated definition. They are two different function templates, one declared and the other one both declared and defined.
In other terms, in your code you are declaring a friend function but you never define it. On the other side, you introduced a free function template that cannot read the data member for it's private and the function isn't a friend one of A<Derived>.
See it running on wandbox.
Related
This is a bit of a complicated pattern that doesn't fit well with friendship. Maybe I have to reconsider the design, but for now I'm just interested whether it is possible to make this work. The problem is that I can't declare class A template explicit instantiation (with incomplete class B as a template argument) that I want to use in a function specialization declaration that I want to use as a friend declaration in the definition of B.
namespace ns
{
template<class ElemT>
void assem_elem(ElemT& elem);
template<class CompT>
class ElemTempl
{
public:
ElemTempl()
{
assem_elem(*this);
}
CompT comp;
};
namespace el { class Comp; }
template class ElemTempl<el::Comp>; // error: 'ns::ElemTempl<ns::el::Comp>::comp' uses undefined class 'ns::el::Comp'
using Elem = ElemTempl<el::Comp>;
template<> void assem_elem<Elem>(Elem& elem);
namespace el
{
class Comp
{
friend void ns::assem_elem<Elem>(Elem& elem);
void link(){}
};
}
template<> void assem_elem<Elem>(Elem& elem)
{
elem.comp.link();
}
}
int main()
{
ns::Elem el{};
return 0;
}
Update:
I came up with two solutions. First, I can just remove
template class ElemTempl<el::Comp>;
line at all. The next line
using Elem = ElemTempl<el::Comp>;
seems to be a declaration of the instantiation(?). Also, even without using line I can write
template<> void assem_elem<ElemTempl<el::Comp>>(ElemTempl<el::Comp>& elem);
directly and this will work. But why? I can't do this with regular classes. At least I must say something like <class RegularClass>, not just <RegularClass>.
The second solution is using a class and passing it through the element's template parameters:
namespace ns
{
template<class CompT, class AssemT>
class ElemTempl
{
public:
ElemTempl()
{
AssemT{ *this };
}
CompT comp;
};
class Assem;
namespace el
{
class Comp
{
friend ns::Assem;
void link() {}
};
}
using Elem = ElemTempl<el::Comp, Assem>;
class Assem
{
public:
Assem(Elem& elem) { elem.comp.link(); }
};
}
But here is also some thing that needs clarification. Class Assem uses Elem, thus it instantiates Elem, but Elem needs Assem to be instantiated and Assem is not defined yet. How can this work?
You seems to confound explicit instantiation and template specialization.
With template class ElemTempl<el::Comp>; (explicit instantiation), you instantiate the full class, and CompT comp; requires a complete type, whereas el::Comp is just forward declared.
using Elem = ElemTempl<el::Comp>; is just the definition of an alias. no instantiation done.
template<> void assem_elem<Elem>(Elem& elem); declares a specialization, Elem might be incomplete.
Class Assem uses Elem, thus it instantiates Elem, but Elem needs Assem to be instantiated and Assem is not defined yet. How can this work?
Elem by itself only need forward declaration to be valid.
The class requires complete type el::Comp when instantiated.
The constructor ElemTempl::ElemTempl requires complete Assem when instantiated.
Constructors and methods are not instantiated when class is implicitly instantiated, but they are when the class is explicitly instantiated.
Why am I getting an error message when calling unary + with operator syntax? If I call it with function syntax, it is OK. Live demo.
template <int size>
struct Buffer { char buf[size]; };
template <class T>
struct Wrapper { void operator+() {} };
Wrapper<Buffer<-5>> a;
void f1() { +a; } // error: Buffer<-5>::buf has negative size
void f2() { a.operator+(); } // OK
The unqualified lookup invokes ADL, which needs to know if there are any friend functions defined in the associated classes. Buffer<-5> is one such, so it is instantiated. The fact that it’s syntactically obvious that it declares no friends doesn’t change the fact that the check for same involves completing the class type, which fails.
As an example let's put Buffer into namespace N, and operator+ into Buffer. If a's type is Wrapper<N::Buffer<5>> (5 rarher than -5), operator+ is found by ADL, and the code compiles (live demo):
template <class T>
struct Wrapper {};
namespace N {
template <int size>
struct Buffer {
template <class T> friend void operator+(const Wrapper<T>&) {}
char buf[size];
};
}
Wrapper<N::Buffer<5>> a;
void f1() { return +a; }
I was messing around with some code when this weird behavior occurred :
In the first test, base templated function, user and templated specialization lie within the same namespace and it behaves as I expect :
namespace Test1
{
template <typename V, typename T>
int doFoo(V& a_visitor, T& a_value)
{
return 0;
}
struct Foo
{
template <typename T>
int process(T const& a_value)
{
return doFoo(*this, a_value);
}
};
template <typename T>
int doFoo(Foo& a_vis, T const& a_ptr)
{
return 1;
}
}
int main()
{
int const k{ 42 };
return Test1::Foo{}.process(k); // returns 1
}
but when I move base templated function and its specialization in another namespace, the base one is selected :
namespace Test2
{
namespace b
{
template <typename V, typename T>
int doBar(V& a_visitor, T& a_value)
{
return 0;
}
}
struct Bar
{
template <typename T>
int process(T const& a_value)
{
return b::doBar(*this, a_value);
}
};
namespace b
{
template <typename T>
int doBar(Bar& a_vis, T const& a_ptr)
{
return 1;
}
}
}
int main()
{
int const k{ 17 };
return Test2::Bar{}.process(k); // returns 0
}
EDIT I can do even weirder : in example 1 if I replace call to doFoo with Test1::doFoo I get the wrong behavior again !
Could anyone explain me what is going on here ? How can I do if I really need struct Bar not to be within namespace b ?
To begin with, those aren't specializations, those are overloads. Completely different function templates that aren't related to each other.
The behavior you see is consistent with argument-dependent lookup. When encountering an unqualified function call, the compiler builds an overload set by examining the namespaces associated with each argument to the function call.
Normally this won't find declarations the come "after", but templates are special. In templates lookup for a dependent name, such as a function call that depends on a template parameter (the type of a_value), is performed after the template is instantiated, and not at the point of definition. That occurs in main, after the namespace is complete and all overloads are available, and so ADL finds the second overload.
This is also why when you qualify the call by Test1 you no longer find the second overload. That negates ADL, and only allows for overloads that appear prior to the point of the call. The easiest way to resolve it would probably be to delay the definition of process until all overloads are available, as other answers indicate.
When does a template end?
Let's have a look at this code:
template <class T>
class thatClass
{
T a, b;
thatClass (T x, T y) {a = x; b = y;}
};
template <class T>T aFunc(T one, T two)
{
return one+two;
}
So when does template <class T> end? Does it always end after at the end of a class or function definition or what? And why can't you just use the one template you declared for both classes and functions, so in this case, I could use the template parameter T for both the function aFunc and for the class definition?
The scope of the template parameter ends with the scope of the templated subject:
template <class T>
class thatClass
{
T a, b;
thatClass (T x, T y) {a = x; b = y;}
}; // << ends here
template <class T>T aFunc(T one, T two)
{
return one+two;
} // << ends here
And why can't you just use the one template you declared for both classes and functions, so in this case, I could use the template parameter T for both the function aFunc and for the class definition?
You can't because template parameter scope is always bound to a class/struct or function definition. That's defined as such in the language.
One could think of templating a whole namespace, but that's not an available language feature, and I'm not sure if that would be a good idea at all.
As you seem to be confused I'll add some variations:
template <class T>
class thatClass
{
T a, b;
thatClass (T x, T y) {a = x; b = y;}
// A member funcion that uses the same template parameter and accesses
// the class member variables
T aFunc() { return a+b; }
// A static member funcion that uses the same template parameter and
// calculates the result from the parameters
static T aStaticFunc(T one, T two) { return one+two; }
};
I've encountered some code which I think should compile, but doesn't. So I'm hoping some of the local standards experts here at SO can help :-).
I basically have some code which resembles this:
#include <iostream>
template <class T = int>
class A {
public:
class U {
};
public:
U f() const { return U(); }
};
// test either the work around or the code I want...
#ifndef USE_FIX
template <class T>
bool operator==(const typename A<T>::U &x, int y) {
return true;
}
#else
typedef A<int> AI;
bool operator==(const AI::U &x, int y) {
return true;
}
#endif
int main() {
A<int> a;
std::cout << (a.f() == 1) << std::endl;
}
So, to describe what is going on here. I have a class template (A) which has an internal class (U) and at least one member function which can return an instance of that internal class (f()).
Then I am attempting to create an operator== function which compares this internal type to some other type (in this case an int, but it doesn't seem to matter).
When USE_FIX is not defined I get the following error:
test.cc: In function 'int main()':
test.cc:27:25: error: no match for 'operator==' in 'a.A<T>::f [with T = int]() == 1'
Which seems odd, because I am clearly (I think) defining a templated operator== which should cover this, in fact if I just do a little of the work for the compiler (enable USE_FIX), then I no longer get an error. Unfortunately, the "fix" doesn't work generically, only for a specific instantiation of the template.
Is this supposed to work as I expected? Or is this simply not allowed?
BTW: if it matters I am using gcc 4.5.2.
The problem with const typename A<T>::U &x is that U is a dependent type and the compiler cannot deduce T from the argument (this is one of the nondeduced context).
You could, for example, have two specializations of A:
class X { };
class Y { };
class Z { };
template <> class A<X> {
public:
typedef Z U;
};
template <> class A<Y> {
public:
typedef Z U;
};
If you then call:
Z a;
a == 1;
what should the compiler deduce T as? X? Y?
One solution in this particular case is to declare operator== as a nontemplate friend inside of the class template:
template <class T = int>
class A {
public:
class U {
};
friend bool operator==(const U& x, int y) {
return true;
}
public:
U f() const { return U(); }
};
template <class T>
bool operator==(const typename A<T>::U &x, int y) {
return true;
}
Using this template, it is not permissible (or sometimes possible) to deduce the template parameter T from the type of x. It is what is known as a non-deducible context. (E.g. Somebody could specialize A for a different parameter, say double and make A<double>::U a typedef for A<int>::U.)
There is no workaround, you would have to explicitly specify the template parameter which for operator== makes for ugly syntax.
It is not allowed for rather obvious reasons. In general case there's really no way the compiler can deduce the template argument from your call to operator ==. Apparently you assumed that the nested type U uniquely defines the enclosing A specialization. That is not true, which can be illustrated by the following example with two explicit specializations of A
template <> class A<int> {
public:
class U {};
};
template <> class A<double> {
public:
typedef A<int>::U U;
};
In this case, if you call templated operator == with an argument of type A<int>::U the compiler cannot deduce template argument T for templated operator ==. Should T be int or double? There's no way to say.
In order to avoid these ambiguities such situations are called non-deduced contexts. Deducing the enclosing class template arguments from a nested type is an example of non-deduced context.