3

So I've been trying to understand variadic templates a little bit more, My goal was to receive all types, expand them, and print them.. I was able to do it in for a function(found some examples) but I was not able to do it for a class Here I am trying to build a 'Master' to hold many 'Slaves', each Slave is supposed to have a Slave_T inherit from him and then know it's type, and print it on c'tor.

But for some reason I am not able to fix the ambigious compilation error.. I was trying to avoid passing any of the type as parameters to the function.. I tried with enable_if or true/false_type convensions but was unable, anyone has any idea? This is my code:(function expansion included)

Function expansion that works:

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;
}

Class Expansion that I need help with:

#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;
}

Thanks!

Alon

Alon
  • 1,776
  • 13
  • 31

1 Answers1

4

First of all: To allow polymorphic templates, you have defined a non-templated pure-virtual base class Slave. Your template class Slave_T must inherit from it (You want a std::vector containing heterogeneous templates, right?). And note that you are using the virtual function defined by the base class Slave. I think you have forgotten to write the base class list before struct Slave_T :)

Second: In that override of the virtual function Type(), you have written Slave<T>::type instead of Slave_T<T>::type. Also note that this sentence needs typename keyword before it, because is a reference to a dependent scope.. But, on the other hand, you have access to the Slave_T template param T, so, why you don't simply use T? :)

Third: Prefer to use std::make_shared instead of a raw new sentences.

Fourth: Prefer std::string instead of C-style raw-strings.

Edit after code fixes:

Template param T of private function MakeSlave() shadows the class variadic template param T. You must have to use other name.

By the way, the error is that in the overloads of makeslave are ambiguous: You have defined a version with template params T and variadic Params, that is, the tipicall HEAD TAIL recursive approach used with variadic packs. But, on the other hand, you also define a version with only one template param. Note that this make ambiguity with the first version, because a variadic pack can be empty, so in your base case the compiler donesn't know what version use.

Solution:

You could use a sentinel type to track when the param list is completely processed. You use this sentinel to enable/disable the base case to avoid the ambiguity:

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
        }

See it in action: http://ideone.com/FqMPXh#

Community
  • 1
  • 1
Manu343726
  • 13,969
  • 4
  • 40
  • 75
  • That is all very true, yet it does not answer my question, I wasn't asking how to implement Slave & Slave_T, I was just doing some scribbles there, my question is how to compile it with the variadic tempaltes syntax, as it says it has ambiguity problems for the last iteration... – Alon Aug 03 '13 at 10:10
  • @Alon First fix the errors (Points one and two). Then we could solve your problem – Manu343726 Aug 03 '13 at 10:12
  • @Alon and please, give me the compiler errors messages and where they be produced – Manu343726 Aug 03 '13 at 10:13
  • Ok, fixed that at least(can't believe I forgot inheritence..), the compiler errors are easily reproduced (Copy it & try to compile), it would say amiguity calls at the MakeSlave call for the last iteration where Rest is one(or zero) – Alon Aug 03 '13 at 10:15
  • @Alon. Sorry. See the last edit. I fixed the code. Accept please – Manu343726 Aug 03 '13 at 10:45