Jonathan Wood
Jonathan Wood

Reputation: 67175

How to alllow template class constructor to support different number of arguments depending on base type?

I have code similar to the following:

class CIntColumn : public CColumn
{
public:
    CIntColumn(LPCTSTR pszName)
    {
    }
    // Etc.
}

class CTextColumn : public CColumn
{
public:
    CTextColumn(LPCTSTR pszName, int nMaxLength)
    {
    }
    // Etc.
}

template<typename BASE_TYPE>
CNullableColumn : public BASE_TYPE
{
public:
    // Questions about code that goes here
}

class CNullableIntColumn : public CNullableColumn<CIntColumn>
{
public:
    CNullableIntColumn(LPCTSTR pszName)
        : CNullableColumn<CIntColumn>(pszName)
    {
    }
}

class CNullableTextColumn : public CNullableColumn<CTextColumn>
{
public:
    CNullableTextColumn(LPCTSTR pszName, int nMaxLength)
        : CNullableColumn<CTextColumn>(pszName, nMaxLength)
    {
    }
}

As is, this code won't compile because CNullableIntColumn needs to pass a single argument to the base class, and CNullableTextColumn needs to pass two arguments to the base class.

Is there any trick to modify my CNullableColumn<> template class so it supports both cases? Ideally, I could use some sort of conditional compilation but C++ doesn't seem to support they kind that would be needed here.

Upvotes: 0

Views: 466

Answers (2)

Jonathan Wood
Jonathan Wood

Reputation: 67175

Building on what Xirema seemed to be originally suggesting, the following appears to work.

template <typename BASE_TYPE>
class CNullableColumn : public BASE_TYPE
{
protected:
    template<typename T1>
    CNullableColumn<BASE_TYPE>(T1 pszName)
        : BASE_TYPE(pszName)
    {
    }

    template<typename T1, typename T2>
    CNullableColumn<BASE_TYPE>(T1 pszName, T2 nMaxLength)
        : BASE_TYPE(pszName, nMaxLength)
    {
    }
}

Upvotes: 1

Xirema
Xirema

Reputation: 20386

You need a generic forwarding constructor that will take any number of arguments and pass them to the base class for CNullableColumn:

template<typename BASE_TYPE>
class CNullableColumn : public BASE_TYPE {
public:
    template<typename ... Args>
    CNullableColumn(Args && ... args) :
        BASE_TYPE(std::forward<Args>(args)...) {}
};

This should cause all your later uses of CNullableTextColumn and CNullableIntColumn to forward their arguments correctly to their roots.


So if you know in advance that each type will only ever have exactly 1 argument, except for one or two specific types that will have two, you might prefer template specialization instead:

template<typename BASE_TYPE>
class CNullableColumn : public BASE_TYPE {
public:
    CNullableColumn(LPCTSTR pszName) :
        BASE_TYPE(pszName) {}
};

//Specialization
template<>
class CNullableColumn<CTextColumn> : public CTextColumn {
public:
    CNullableColumn(LPCTSTR pszName, int maxLength) :
        CTextColumn(pszName, maxLength) {}
};

//Could create other specializations for other types with different #s of arguments, if needed

Now, the disadvantage of Template Specialization is that you have to be clever in your implementation to avoid duplicating code. But it'll at least elegantly solve the problem you're trying to deal with without just turning everything into a Varargs nightmare.

Upvotes: 1

Related Questions