Befriending a template class: Compile error

2019-07-09 01:29发布

问题:

I'm trying to use the Pointer to Implementation Idiom to hide the fact that I am using a Concurrency::unbounded_buffer (from VC++ 2010). The problem is, I'm doing it with templates and got stuck in a compile error. Here's the code:

BlockingQueue.h

#pragma once

namespace DRA{
namespace CommonCpp{
    //Forward declaration
    template <class Element> class BlockingQueueImpl;

    template <class Element>
    class BlockingQueue{
        BlockingQueueImpl<Element>* m_pImpl;
    //Forbid copy and assignment
        BlockingQueue( const BlockingQueue& );
        BlockingQueue& operator=( const BlockingQueue& );
    public:
        BlockingQueue();
        ~BlockingQueue();
        void Push( Element newElement );
        Element Pop();
    };
}
}

BlockingQueue.cpp

#include "BlockingQueue.h"
#include "BlockingQueueImpl.h"

using namespace DRA::CommonCpp;

BlockingQueue<class Element>::BlockingQueue():
    m_pImpl( new BlockingQueueImpl<Element>() ){
}

BlockingQueue<class Element>::~BlockingQueue(){
    //Let the implementation's destructor handle pending Pops
    delete m_pImpl;
}

template<class Element>
void BlockingQueue<Element>::Push( Element newElement ){
    m_pImpl->Push( newElement );
}

template<class Element>
Element BlockingQueue<Element>::Pop(){
    return m_Impl->Pop();
}

BlockingQueueImpl.h

#pragma once

#include <agents.h> //This is VS2010 Exclusive (Concurrency Runtime)

namespace DRA{ namespace CommonCpp{ template <class Element> class BlockingQueue; } }

namespace DRA{
namespace CommonCpp{
template <class Element>
    class BlockingQueueImpl{
        Concurrency::unbounded_buffer<Element>  m_Queue;
    //Only friends can use this class
        friend class BlockingQueue<Element>;
        BlockingQueueImpl();
        ~BlockingQueueImpl();
    //Forbid copy and assignment
        BlockingQueueImpl( const BlockingQueueImpl& );
        BlockingQueueImpl& operator=( const BlockingQueueImpl& );
    //Members
        void Push( Element newElement );
        Element Pop();
};
}
}

BlockingQueueImpl.cpp

#include "BlockingQueueImpl.h"

using namespace DRA::CommonCpp;

BlockingQueueImpl<class Element>::BlockingQueueImpl(){
}

BlockingQueueImpl<class Element>::~BlockingQueueImpl(){
}

template<class Element>
void BlockingQueueImpl<class Element>::Push( Element newElement ){
    send( m_Queue, newElement );
}

template<class Element>
Element BlockingQueueImpl<class Element>::Pop(){
    return receive( m_Queue );
}

The error is:

BlockingQueueImpl.cpp(12): error C2649: 'typename' : is not a 'class'

I tried adding class to the template parameter in the friend declaration, but it didn't work

回答1:

Your problem is incorrect syntax of the template function definition. The second class keyword should be removed.

template<class Element>
void BlockingQueueImpl< /*class*/ Element>::Push( Element newElement ){
    send( m_Queue, newElement );
}

And yes, you should not separate template declaration and its implementation.



回答2:

Addition to existing answer:

error C2649: 'typename' : is not a 'class'

I had the same error message, but my code looked slightly different:

template< class T, class TNodeType = SimpleTreeNode<T> ...>
class Tree {
    friend class TNodeType; // *** Turns out: WRONG friend declaration

The same applies as in existing answer: Leave out class keyword (compiler already knows it's a type I'd assume):

template< class T, class TNodeType = SimpleTreeNode<T> ...>
class Tree {
    friend TNodeType; // *** OK: note missing `class`keyword


回答3:

The thing with templates is that they are header-only. You can't put template implementations in the cpp files because the entire template needs to be available when it is instantiated.

I doubt you can use this idiom with templates at all.



回答4:

templates basically give a compiler a template to construct a code. It's not "real" code in the sense that it can be used and compiled as it.
This means that when you instantiate a template (use the class or templated method somewhere with a concrete class type) the compiler needs to see the actual template code to generate the right code.

What all this basically means is that the templated code MUST be in the headers, and included to any other file that uses the templated code.