Рекурсивное расширение шаблонов Variadic для классов и функций

Итак, я пытался немного больше понять вариативные шаблоны. Моя цель состояла в том, чтобы получить все типы, расширить их и распечатать. Я смог сделать это для функции (нашел несколько примеров), но я не был в состоянии сделать это для класса. Здесь я пытаюсь создать «Мастер», чтобы содержать много «Рабов», каждый раб должен иметь наследуемый от него Slave_T, а затем знать его тип и печатать его на c'tor.

Но по какой-то причине я не могу исправить неоднозначную ошибку компиляции. Я пытался избежать передачи какого-либо типа в качестве параметров функции. Я пытался с соглашениями enable_if или true/false_type, но не смог, кто-нибудь знает ? Это мой код: (расширение функций включено)

Расширение функций, которое работает:

template<typename T, typename... Targs>
void Print(T value, Targs... Fargs) // recursive variadic function
{
    Print<T>();
    Print(Fargs...); // recursive call
}

template<typename T>
void Print(T = NULL)
{
    std::cout << typeid(T).name() << endl;
}

Расширение класса, с которым мне нужна помощь:

#include <iostream>
#include <vector>
#include <type_traits>
#include <memory>
using namespace std;



struct Slave {
    virtual char const* Type() = 0;
};

template<typename T>
struct Slave_T : public Slave{
    virtual char const* Type() {
        return typeid(T).name();
    }
};

template <typename ...T>
struct Master {
    Master()
    {
        MakeSlave<T...>();
        cout << "All Slaves:" << endl;
        for (auto const& slave : v){
            cout << slave ->Type() << endl;
        }
    }
private:

    template<typename T, typename ...Rest>
    void MakeSlave()
    {
        MakeSlave<Rest...>(); MakeSlave<T>();
    }

    template<typename T>
    void MakeSlave()                    {
        v.push_back(new Slave_T<T>());
    }

    vector<shared_ptr<Slave>> v;
};

int main()
{
    Master<int, double, char> m;
    //Print("Hello", '!', 123, 123);
    return 0;
}

Спасибо!

Алон


person Alon    schedule 03.08.2013    source источник


Ответы (1)


Прежде всего: чтобы разрешить полиморфные шаблоны, вы определили чисто виртуальный базовый класс Slave без шаблонов. Ваш класс шаблона Slave_T должен наследоваться от него (вам нужен класс std::vector, содержащий разнородные шаблоны, верно?). Обратите внимание, что вы используете виртуальную функцию, определенную базовым классом Slave. Я думаю, вы забыли написать список базовых классов перед struct Slave_T :)

Во-вторых: в этом переопределении виртуальной функции Type() вы написали Slave<T>::type вместо Slave_T<T>::type. Также обратите внимание, что перед этим предложением необходимо ключевое слово typename, потому что является ссылкой на зависимую область.. Но, с другой стороны, у вас есть доступ к параметру шаблона Slave_T T, так почему бы вам просто не использовать T? :)

Третье: вместо этого лучше использовать std::make_shared. сырых new предложений.

Четвертое: предпочитайте std::string вместо стиля C необработанные строки.

Изменить после исправления кода:

Параметр шаблона T частной функции MakeSlave() затеняет параметр вариационного шаблона класса T. Вы должны использовать другое имя.

Кстати, ошибка в том, что в перегрузках makeslave неоднозначны: Вы определили версию с параметрами шаблона T и вариативным Params, то есть типичный HEAD TAIL рекурсивный подход, используемый с вариативными пакетами. Но, с другой стороны, вы также определяете версию только с одним параметром шаблона. Обратите внимание, что это создает двусмысленность с первой версией, потому что вариативный пакет может быть пустым, поэтому в вашем базовом случае компилятор не знает, какую версию использовать.

Решение:

Вы можете использовать тип дозорного, чтобы отслеживать, когда список параметров полностью обработан. Вы используете этот часовой для включения/отключения базового случая, чтобы избежать двусмысленности:

template <typename ...T>
    struct Master {
        Master()
        {
            MakeSlave<T...,_my_centinel_type>();
            cout << "All Slaves:" << endl;
            for (auto const& slave : v){
                cout << slave ->Type() << endl;
            }
        }
    private:
        struct _my_centinel_type {};

        template<typename U, typename ...Rest>
        typename std::enable_if<!std::is_same<U,_my_centinel_type>::value , void>::type MakeSlave()
        {
                v.push_back( std::make_shared<Slave_T<U>>());
                MakeSlave<Rest...>();
        }

        template<typename U>
        typename std::enable_if<std::is_same<U,_my_centinel_type>::value , void>::type MakeSlave(){
            //The base case does anything
        }

Посмотрите это в действии: http://ideone.com/FqMPXh#.

person Manu343726    schedule 03.08.2013
comment
Это все очень верно, но это не отвечает на мой вопрос, я не спрашивал, как реализовать Slave & Slave_T, я просто делал там какие-то каракули, мой вопрос в том, как скомпилировать его с синтаксисом variadic tempaltes, как он говорит у него есть проблемы с неоднозначностью для последней итерации... - person Alon; 03.08.2013
comment
@Alon Сначала исправьте ошибки (пункты один и два). Тогда мы могли бы решить вашу проблему - person Manu343726; 03.08.2013
comment
@ Алон, и, пожалуйста, дайте мне сообщения об ошибках компилятора и где они создаются - person Manu343726; 03.08.2013
comment
Хорошо, исправлено, по крайней мере, (не могу поверить, что я забыл о наследовании ..), ошибки компилятора легко воспроизводятся (скопируйте его и попробуйте скомпилировать), он будет говорить о двусмысленных вызовах в вызове MakeSlave‹Rest...› для последняя итерация, где Rest равен единице (или нулю) - person Alon; 03.08.2013
comment
@Алон. Прости. Смотрите последнее редактирование. Я исправил код. Примите, пожалуйста - person Manu343726; 03.08.2013