Указатель на глобальный экземпляр класса шаблона в качестве параметра шаблона?

Я пытаюсь реализовать общую версию кода ниже:

#include <iostream>

class ContainerA
{
    public:
        ContainerA( int newData )
            : mData_(newData)
        {}
        int mData_;
};

class ContainerB
{
    public:
        ContainerB( int newData )
            : mData_(newData)
        {}
        int mData_;
};

ContainerA staticInstanceA( 3 );
ContainerB staticInstanceB( 11 );

template< ContainerA* ptrToContainer >
class WorkerOnA
{
    public:
        WorkerOnA( )
            : mPtrToContainer_(ptrToContainer)
        {}

        void operator()()
        {
            std::cout << "Data = " << mPtrToContainer_->mData_ << '\n';
        }

    private:
        ContainerA* mPtrToContainer_;
};

template< ContainerB* ptrToContainer >
class WorkerOnB
{
    public:
        WorkerOnB( )
            : mPtrToContainer_(ptrToContainer)
        {}

        void operator()()
        {
            std::cout << "Data = " << mPtrToContainer_->mData_ << '\n';
        }

    private:
        ContainerB* mPtrToContainer_;
};

int main( )
{
    WorkerOnA<&staticInstanceA> workerOnAInstance;
    WorkerOnB<&staticInstanceB> workerOnBInstance;

    workerOnAInstance();
    workerOnBInstance();

    return 0;
}

То, что я хотел бы иметь (если это вообще возможно), — это один класс-шаблон Worker, экземпляр которого можно создать для работы с любым контейнером, например:

template< ?? ptrToContainer >
class WorkerOnAnyContainer
{
    public:
        WorkerOnA( )
            : mPtrToContainer_(ptrToContainer)
        {}

        void operator()()
        {
            std::cout << "Data = " << mPtrToContainer_->mData_ << '\n';
        }

    private:
        ?? mPtrToContainer_;
};

Однако по прошествии нескольких часов я все еще не могу понять, какими должны быть «???». Может у мастера шаблонов есть идея?

Обновление 1: исправлена ​​ошибка в 'operator()' рабочих процессов (ptrToContainer -> mPtrToContainer_). Простите за это.

Обновление 2: у меня кое-что работает, но мне все равно было бы любопытно, есть ли у кого-нибудь идея получше. Например, было бы неплохо иметь один параметр-шаблон. Кто-нибудь знает, могут ли «параметры шаблона шаблона» помочь в этой ситуации?

template< class TContainer, TContainer* ptrToContainer >
class Worker
{
    public:
        Worker( )
            : mPtrToContainer_(ptrToContainer)
        {}

        void operator()()
        {
            std::cout << "Data = " << mPtrToContainer_->mData_ << '\n';
        }

    private:
        TContainer* mPtrToContainer_;
};

Спасибо д


person Dragos    schedule 15.04.2012    source источник
comment
О, я вижу, что мой отредактированный ответ излишен, учитывая ваше редактирование :-) Хм. Параметры шаблона-шаблона предназначены для случаев, когда у вас есть тип, который принимает параметр шаблона, и вы используете этот тип в качестве шаблона. Не думаю, что это применимо здесь. Я попытался написать вспомогательную функцию для определения типа, но она тоже не работает, потому что значение указателя является частью сигнатуры рабочего типа. +1, это интереснее, чем я думал сначала.   -  person Cameron    schedule 15.04.2012
comment
@Cameron Мой производственный компилятор принадлежит Intel, который имеет некоторые функции С++ 11: software.intel.com/en-us/articles/. Какие новые функции потребуются для объединения параметров шаблона?   -  person Dragos    schedule 15.04.2012


Ответы (1)


Я попробую. Как насчет того, чтобы изменить ваш шаблон так, чтобы ему был задан тип в качестве параметра, а не сам указатель? Вы все еще можете передать указатель на конструктор:

template< typename TContainer >
class WorkerOnAnyContainer
{
    public:
        WorkerOnA( TContainer* ptrToContainer )
            : mPtrToContainer_(ptrToContainer)
        {}

        void operator()()
        {
            std::cout << "Data = " << mPtrToContainer_->mData_ << '\n';
        }

    private:
        TContainer* mPtrToContainer_;
};

Тогда вы можете использовать его как:

WorkerOnAnyContainer<ContainerA> workerOnAInstance(&staticInstanceA);

Поскольку вы хотите сохранить дизайн указателя как параметра шаблона, вы можете сделать что-то вроде этого:

template< typename TContainer, TContainer* ptrToContainer >
class WorkerOnAnyContainer
{
    public:
        WorkerOnA()
            : mPtrToContainer_(ptrToContainer)
        {}

        void operator()()
        {
            std::cout << "Data = " << ptrToContainer->mData_ << '\n';
        }

    private:
        TContainer* mPtrToContainer_;
};

И используйте его как:

WorkerOnAnyContainer<ContainerA, &staticInstanceA> workerOnAInstance;

Но это немного запутанно, так как вам нужны два аргумента шаблона, и первый кажется избыточным. Я не уверен, что это возможно решить с помощью С++ 03, но я подумал, что в С++ 11 можно будет создать вспомогательный метод, который может сделать для нас вывод типа:

template<typename T>
auto CreateWorker(T* container) -> WorkerOnAnyContainer<T, container>
{
    return WorkerOnAnyContainer<T, container>();
}

Но, поскольку компилятор ожидает, что функция будет работать для параметров, не являющихся константами времени компиляции, это не компилируется (GCC 4.6.3):

use of parameter 'container' outside function body

Оказывается, вы не единственный, кто пытается это сделать. По-видимому, вы не можете создать вспомогательный метод таким образом даже в C++11.

Единственное, что я могу придумать, это действительно работает, это использовать макрос (я знаю, я знаю):

#define CreateWorker(container) WorkerOnAnyContainer<decltype(container), &container>()

Затем использовать его так же просто, как:

auto worker = CreateWorker(staticInstanceA);    // Note no `&'

Это использует auto и простой decltype, обе функции C++11, которые компилятор Intel C++ поддерживает с версии v12 (хотя я не тестировал этот код ни с чем, кроме GCC). Будучи макросом, он, конечно, немного хрупкий.

Посмотрите в действии!

person Cameron    schedule 15.04.2012
comment
Спасибо за ответ. Собственно, этим я и пользовался до сих пор. Однако это часть более крупной числовой модели, и по соображениям производительности теперь я хочу убедиться, что указатель на контейнер встроен в Worker во время компиляции, чтобы помочь компилятору встроить некоторые методы (опущенные в моем примере) из Container-класс, который вызывается из Workers. Есть еще идеи? - person Dragos; 15.04.2012
comment
Большое спасибо за ответ! Кажется, мы придумали одно и то же решение, но не синхронизировались. Знаете ли вы, можно ли комбинировать два параметра шаблона? Это, конечно, побочный вопрос, но я думаю, что это может облегчить жизнь моим пользователям :). - person Dragos; 15.04.2012
comment
@Dragos: Хорошо, это намного сложнее, чем я думал :-( Единственное решение (компиляция), которое я могу придумать, использует макрос (вздох). См. мое редактирование. - person Cameron; 15.04.2012