Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++: Correct syntax for friending a template type member of template parameter?

I have a class that takes a template type parameter (tTRAIT). I want to friend a template type member alias of tTRAIT, but I can't figure out the syntax. (Is this even possible?).

template <bool bBOOL>
struct SFoo {};

struct STrait
    {
        template <bool bBOOL>
        using TFoo = SFoo<bBOOL>;
    };

template <typename tTRAIT>
struct SBar
    {
        template <bool bBOOL>
        friend typename tTRAIT::template TFoo<bBOOL>;
    };

SBar<STrait> bar;

Clang's error (on the friend line) is:

error: friend type templates must use an elaborated type

I have tried exhausting all possible combinations I can think of:

friend tTRAIT::TFoo;
friend tTRAIT::template TFoo;
friend typename tTRAIT::TFoo;
friend typename tTRAIT::template TFoo;
template <bool bBOOL> friend tTRAIT::TFoo;
template <bool bBOOL> friend tTRAIT::TFoo<bBOOL>;
template <bool bBOOL> friend tTRAIT::template TFoo;
template <bool bBOOL> friend tTRAIT::template TFoo<bBOOL>;
template <bool bBOOL> friend typename tTRAIT::TFoo;
template <bool bBOOL> friend typename tTRAIT::TFoo<bBOOL>;
template <bool bBOOL> friend typename tTRAIT::template TFoo;
template <bool bBOOL> friend typename tTRAIT::template TFoo<bBOOL>;

I have also tried using using, but it doesn't seem to help.

As an ugly hack (which only works for bool parameters), I can get it to work by friending each specialization manually.

friend typename tTRAIT::template TFoo<false>;
friend typename tTRAIT::template TFoo<true >;

But that's yucky.

Does anyone know how to do this, or if this can be done?

like image 962
xaxazak Avatar asked Apr 29 '15 13:04

xaxazak


People also ask

How do I declare a friend template class in C++?

class B{ template<class V> friend int j(); } template<class S> g(); template<class T> class A { friend int e(); friend int f(T); friend int g<T>(); template<class U> friend int h(); }; Function e() has a one-to-many relationship with class A . Function e() is a friend to all instantiations of class A .

What are template arguments enlist types of template arguments?

A template argument for a template template parameter is the name of a class template. When the compiler tries to find a template to match the template template argument, it only considers primary class templates. (A primary template is the template that is being specialized.)

What can the template parameter in C++ template definition be?

A template parameter is a special kind of parameter that can be used to pass a type as argument: just like regular function parameters can be used to pass values to a function, template parameters allow to pass also types to a function.

Can we declare a template function as the friend of the class?

A template friend declaration can name a member of a class template A, which can be either a member function or a member type (the type must use elaborated-type-specifier).


2 Answers

I don't think this is possible. From standard draft N4296:

§ 14.5.4/1 [temp.friend]

A friend of a class or class template can be a function template or class template, a specialization of a function template or class template, or a non-template function or class.

This doesn't include alias templates, so the standard doesn't support what you want to do. This is perhaps due to the following excerpt (emphasis mine):

§ 14.5.7/1 [temp.alias]

A template-declaration in which the declaration is an alias-declaration (Clause 7) declares the identifier to be a alias template. An alias template is a name for a family of types.

An alias template names a separate family of types, so even if there were some syntax which made sense for this, you would be friending the alias template rather than the template which is being aliased.

For example, GCC will compile this (Clang won't), but you won't actually be able to use the friendship in any reasonable way:

template <bool B>
using MyTFoo = typename tTRAIT::template TFoo<B>;

template <bool> friend class MyTFoo; 

Another example of how an alias template is not the same as the aliased template:

template <template <typename...> class A, template <typename...> class B>
struct is_same_template : std::false_type{};

template <template <typename...> class A>
struct is_same_template<A,A> : std::true_type{};

template <typename T> using myvec = std::vector<T>;

//this fails
static_assert(is_same_template<myvec,std::vector>::value, "wat");

Your manual friending with explicit instantiation will work, because the alias template will collapse down to exactly the same type as the aliased template. An analogous example:

//this passes!
static_assert(std::is_same<myvec<int>,std::vector<int>>::value, "wat");
like image 101
TartanLlama Avatar answered Sep 16 '22 12:09

TartanLlama


I could go I step further with Clang 3.4.1 in std=c++11 mode.

This compiles without error :

template

struct SBar
    {
private:
    int j;
public:
        template <bool bBOOL>
        friend struct tTRAIT::TFoo;
        void setJ(int j) {
            this->j = j;
        }
};

But ... I get this warning : warning: dependent nested name specifier 'tTRAIT::' for friend template declaration is not supported; ignoring this friend declaration [-Wunsupported-friend] : friend struct tTRAIT::TFoo;

and I can confirm that the SFoo classes are not friend (the reason for private j ...)

The only way I could have it all to compile and run is :

struct SBar
    {
private:
    int j;
public:
        template <bool bBOOL>
        friend struct sFoo;
        void setJ(int j) {
            this->j = j;
        }
};

Fine, SFoo classes are friends, but it somewhat defeats OP requirement ( template type member of [template parameter]) ...

I currently have no access to a recent gcc but I think we are here on the edge of how compilers interprets the standard. I read the chapter referenced by TartanLlama, but could not make sure if this was intended or not. Maybe my first try would be accepted by gcc ...

like image 35
Serge Ballesta Avatar answered Sep 17 '22 12:09

Serge Ballesta