Link to home
Start Free TrialLog in
Avatar of AlexFM
AlexFM

asked on

Template parameters

Suppose I have template class:

template <class T1, class T2>
class CTemplateClass
{
    ....
};

and client code which uses this template by such way:

CTemplateClass<CClass1, CClass1> obj;

I want to give for this particular client code the opportunity to use this template writing CClass1 only once (because for this client second CClass1 is redundant). Currently I have such definition:

#define CTEMPLATECLASS(ClassName) CTemplateClass<ClassName, ClassName>

and client writes:

CTEMPLATECLASS<CClass1> obj;

However, using #define is not a good style. I need more elegant solution like using additional template class, which gives the same effect.
Avatar of Paul Maker
Paul Maker
Flag of United Kingdom of Great Britain and Northern Ireland image

you could sub class it, see

class _T1
{
public:
      string toString()
      {
            return "_T1";
      }
};

template <class T1,class T2>
class Test
{
public:
      T1 t1;
      T2 t2;

      Test()
      {
            cout<<"Test: "<<t1.toString()<<endl;
            cout<<"Test: "<<t2.toString()<<endl;
      }
};

template <class T1>
class SubTest : public Test<T1,T1>
{

};

int main()
{
      SubTest<_T1> _test;
      return 1;
}
Avatar of Mike-
Mike-

template <class T1, class T2 = T1>

maybe, not sure though.

You can set
template <class T1 = float>

So maybe this works, maybe it doesn't
I believe Mike got the best solution.

template <class T1, class T2 = T1>
....

Then you can call the template with one or two arguments, if only one is given the T2 argument is the same as the first.

And yes, it should work Mike. STL uses such a feature often. Not exactly like this but like this:

template <typename _CharT, typename _TraitsT = char_traits<_CharT> >
....

See? You can even make the second parameter be a more complicated template with a default value depnedong on the first. ifstream for example is a class that is defined this way:

template <typename _CharT, typename _TraitsT = char_traits<_CharT> >
class basic_ifstream { .... };

typedef basic_ifstream<char> ifstream;
typedef basic_ifstream<wchar_t> wifstream;

So you see that ifstream and wifstream isn't really classes at all, they're typedefs.

What I really would like to see in C++ is templated template parameters.

template <typename T, template container<typename U> = std::vector >
class foo {
    container<T> m_c;
    ....
};

foo<int,std::list>   x;
// x.m_c should be of type std::list<int>

and variable number of template arguments:

template <typename T ...>
class foo {
    void (* m_funcptr)(T);
};

foo<int,double>  would have an m_funcptr as:    void (*m_funcptr)(int,double) etc.

Don't know if we will ever see those features. I know that boost is doing a good job of faking variable number of template arguments but it's not exactly faking I would like to see, I'd like The Real Thing so to speak ;)

Oh well.

I would also like to see closures, again boost tries to fake it but again, I would rather see The Real Thing.

Oh well, some day...

Alf
Avatar of AlexFM

ASKER

Mike, I don't want to change the original template, which has another default value for T2 parameter. I want to do something outside of this template.

makerp, you give simple and nice solution. However, inheritance doesn't solve the problem of constructor and operator=. Your code sample works, but when I tries to apply it, I got compilation errors. I changed your code to show what happens:

class _T1
{
public:
    _T1()
    {
        m_str = "default string";
    }

    string toString()
    {
         return m_str;
    }

    string m_str;
};

template <class T1,class T2>
class Test
{
public:

    Test()
    {
    }

    Test(string s)
    {
        t1.m_str = s;
        t2.m_str = s;
    }        

    void MakeTest()
    {
         cout<<"Test: "<<t1.toString()<<endl;
         cout<<"Test: "<<t2.toString()<<endl;
    }

    Test& operator=(string s)
    {
        t1.m_str = s;
        t2.m_str = s;
        return *this;
    }

    T1 t1;
    T2 t2;
};

template <class T1>
class SubTest : public Test<T1,T1>
{

};


int _tmain(int argc, _TCHAR* argv[])
{
    SubTest<_T1> _test;
    _test.MakeTest();                  // OK

    string s = "new string";

    SubTest<_T1> _test1(s);      // not compiled: cannot convert from 'std::string' to 'const SubTest<T1>,
    _test1.MakeTest();

    SubTest<_T1> _test2;      
    _test2 = s;                            // not compiled: binary '=' : no operator found which takes a right-hand operand of type 'std::string'
    _test1.MakeTest();

    return 0;
}


Avatar of AlexFM

ASKER

Salte, templated template parameters are supported in Visual Studio .NET 2003.
ASKER CERTIFIED SOLUTION
Avatar of Salte
Salte

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
>>  Salte, templated template parameters are supported in Visual Studio .NET 2003.

Nice! I hope to see it in other compilers also soon ;) GCC for example ;)

Alf
Avatar of AlexFM

ASKER

Salte, great! This is my test with your solution applied:

class _T1
{
public:
    _T1()
    {
        m_str = "default string";
    }

    string toString()
    {
         return m_str;
    }

    string m_str;
};

template <class T1,class T2>
class Test
{
public:

    Test()
    {
    }

    Test(string s)
    {
        t1.m_str = s;
        t2.m_str = s;
    }        

    T1 t1;
    T2 t2;

    void MakeTest()
    {
         cout<<"Test: "<<t1.toString()<<endl;
         cout<<"Test: "<<t2.toString()<<endl;
    }

    Test& operator=(string s)
    {
        t1.m_str = s;
        t2.m_str = s;
        return *this;
    }
};

template <class T>
struct TestHelper
{
    typedef Test<T, T> type;
};


int _tmain(int argc, _TCHAR* argv[])
{
    TestHelper<_T1>::type _test;
    _test.MakeTest();

    string s = "new string";

    TestHelper<_T1>::type _test1(s);
    _test1.MakeTest();

    TestHelper<_T1>::type _test2;
    _test2 = s;
    _test1.MakeTest();

    return 0;
}

Thanks.
Avatar of AlexFM

ASKER

makerp, your provided a good solution for my initial question. Get your points here:

https://www.experts-exchange.com/questions/20628702/Points-for-makerp.html

Thanks.
AlexFM,
I see in your code that you're using underscore for your variable names.
You should avoid using underscore as the first character for your variable names, functions, and classes.

According to the C++ standard, the underscore character in the beginning of a name is reserved for implementation.

Because of the mis-used underscore, the above code is less portable and can failed to compile on some compilers/platforms.


Salte,
Nice template solution.
Avatar of AlexFM

ASKER

Actually, this is not my underscores.
All rights belong to:

makerp:     _T1
Bill Gates:   _tmain

Anyway, thanks.
AlexFM said,

makerp, your provided a good solution for my initial question. Get your points here:

https://www.experts-exchange.com/questions/20628702/Points-for-makerp.html

I believe the EE board now support splitting points so you don't have to make any "points for makerp" or whatever any more, just  accept more than one answer.

Not sure how exactly it is done though since I seldom ask any questions, I've just seen recently that you some times get two green answers and one or both of them gets "assisted answer" instead of "accepted answer" etc.

Alf
Avatar of AlexFM

ASKER

Yes, you are right. I just wanted to give you all points. After accepting your answer I understood that I could add 50 points and then split them.
AlexFM,
>>makerp:     _T1
>>Bill Gates:   _tmain

Those aren't variables.
I was referring to _test.


   TestHelper<_T1>::type _test;
   _test.MakeTest();

FYI:
Bill Gates can do this according to the C++ standards, because you using his implementation.

In other words, the platform or the compiler can use the underscore in the beginning of a name.  That is who the underscore is reserve for.
But your code, should not be using it for your defined variables, functions, or class name.
Avatar of AlexFM

ASKER

Nice to learn what is variable. EE is a good site!
i think everything in c++ should be prefixed with lots and lots of underscores....
Avatar of AlexFM

ASKER

_Indeed !