We observed a surprising behaviour in our code base, where a friendship relation was failing to be applied. (It is currently compiled only with Clang, version 3.6)
We could reduce it to this minimal example. Let's imagine we have the following template class definitions:
template <int>
class Element
{};
// Forward declaration of FriendBis
template <template <int> class> class FriendBis;
class Details
{
friend class FriendBis<Element>;
int mValue = 41;
};
template <template <int> class>
class FriendBis
{
public:
void useDetails(const Details &aDetails)
{
aDetails.mValue;
}
};
Here, Details
declares that the instantiation of FriendBis
with its single template template parameter substituted with Element
is its friend
. Because of that, the following client code compiles successfully:
FriendBis<Element> fb1;
fb1.useDetails(Details());
Now, let's introduce the additional trait
templated type, whose whole purpose is to define proto
as a template alias for the Element
template:
struct trait
{
template <int N>
using proto = Element<N>;
};
The client code below does not compile:
FriendBis<trait::proto> fb2;
fb2.useDetails(Details());
It is surprising to us, because trait::proto
is an alias for Element
, but one compiles while the other does not.
FriendBis
a friend).An alias template is not synonymous with the type it aliases: trait::proto
and Element
are disparate types. When a template-id refers to the specialization of trait::proto
then it is equivalent to the substituted type. Simply put, trait::proto
is not Element
, but trait::proto<0>
is Element<0>
.
In response to your questions:
Yes, this is expected behaviour
The rationale is that the aliased type could be far more complex than just Element<N>
, it could be something like Element<ElementForInt<N+1>::value>
. The mapping then is non-obvious.
I can't think of a workaround off the top of my head. If you want to check if a template template parameter is the same as some other template and account for alias templates, you can check if instantiations of the two name the same type, e.g. std::is_same<T<0>, Element<0>>
, but I'm not sure how you would make that work in a friend declaration.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With