1

I am trying to build a specialization for a template class with a compile time constant.

The template class looks like this:

template<class TNativeItem, class TComItem = void,
         VARTYPE _vartype = _ATL_AutomationType<TComItem>::type>
class InOutComArray
{
private:
    CComSafeArray<TComItem, _vartype> _safeArray;
    // ...
public:
    InOutComArray(
        TNativeItem* items, size_t length,
        std::function<TComItem(const TNativeItem&)> convertToCom,
        std::function<TNativeItem(const TComItem&)> convertFromCom)
        : _safeArray(length)
    {
        // ...
    }

    // ...
};

Usage would be for example:

InOutComArray<BOOL, VARIANT_BOOL, VT_BOOL>(
    items, length, BOOLToVARIANT_BOOL, VARIANT_BOOLToBOOL));

However, there also exist types that don't require conversion and I wanted to provide a short hand version for this:

InOutComArray<LONG>(items, length);

I tried to implement it like this:

template<class TItem, VARTYPE _vartype = _ATL_AutomationType<TItem>::type>
class InOutComArray<TItem, void, _vartype>
    : public InOutComArray<TItem, TItem, _vartype>
{
public:
    InOutComArray(TItem* items, size_t length)
        : InOutComArray<TItem, TItem, _vartype>(
              items, length, NoConverter<TItem>, NoConverter<TItem>)
    {

    }
};

However, I get the following error:

'_vartype' : default template arguments not allowed on a partial specialization

Is there any way around that?

Daniel Hilgarth
  • 171,043
  • 40
  • 335
  • 443

3 Answers3

3

You first define the default arguments to be void and _ATL_AutomationType<TComItem>::type, so when exactly one argument X is given, you want InOutComArray<X> to be an InOutComArray<X, void, _ATL_AutomationType<void>::type>.

Your partial specialization contradicts this: InOutComArray<X> shall be a InOutComArray<X, X, _ATL_AutomationType<X>::type>.

Depending on what you thik will be more likely second argument, (i.e. void or the same as the first argument), you could make the second argument defaulted to the first one in the first place:

template<class TNativeItem, class TComItem = TNativeItem,
     VARTYPE _vartype = _ATL_AutomationType<TComItem>::type>

That way the behavior of the partial specialization is covered, except for the additional constructor. This can be achieved by using default arguments for the constructor:

template<class TNativeItem, class TComItem = TNativeItem,
     VARTYPE _vartype = _ATL_AutomationType<TComItem>::type>
class InOutComArray
{
public:
InOutComArray(
    TNativeItem* items, size_t length,
    std::function<TComItem(const TNativeItem&)> convertToCom = NoConverter<TNativeItem>(),
    std::function<TNativeItem(const TComItem&)> convertFromCom = NoConverter<TNativeItem>());
};
Arne Mertz
  • 24,171
  • 3
  • 51
  • 90
  • Thanks a lot for this excellent information! This looks very elegant, however, the following scenario isn't solved by this: `InOutComArray(items, length)`. Is there a way to allow this? – Daniel Hilgarth Aug 18 '14 at 13:54
  • The `std::enable_if` doesn't seem to work, I am getting `'type' : is not a member of 'std::enable_if'`. [Looks like](http://stackoverflow.com/questions/6972368/stdenable-if-to-conditionally-compile-a-member-function) it can't be used like this? – Daniel Hilgarth Aug 18 '14 at 13:59
  • An `InOutComArray` would be an `InOutComArray::type>` - I guess that is not what you want? (No idea what VT_I8 might be) If you are looking to "overload" the second template parameter, so you can have either a TComItem or a VARTYPE (whatever that might be). I am not sure if that is possible at all - you will have to specify all the arguments in one of the cases. – Arne Mertz Aug 18 '14 at 14:05
  • What I meant by that was that I can write `InOutComArray` and get a `InOutComArray`. – Daniel Hilgarth Aug 18 '14 at 14:07
  • 2
    @DanielHilgarth: corrected the enable_if part, it's not needed. As for your last comment: you can resolve a `InOutComArray` either to `InOutComArray::type>` or to `InOutComArray`, but not sometimes the former and sometimes the latter. You seem to want both - you will have to decide. – Arne Mertz Aug 18 '14 at 14:26
1

According to the standard §14.5.5/8 Class template partial specializations [temp.class.spec]:

The template parameter list of a specialization shall not contain default template argument values.

Thus, the compiler rightfully complains because in your partial specialization you give default template argument value for VARTYPE _vartype = _ATL_AutomationType<TItem>::type.

101010
  • 41,839
  • 11
  • 94
  • 168
1

Is there any way around that?

Yes, remove the default template argument from the partial specialization. You don't need it.

Per the primary template:

template<class TNativeItem, class TComItem = void,
         VARTYPE _vartype = _ATL_AutomationType<TComItem>::type>
class InOutComArray

These types are equivalent:

InOutComArray<LONG>
InOutComArray<LONG, void, _ATL_AutomationType<TComItem>::type>

And whenever InOutComArray is instantiated with TComItem = void, you will get the partial specialization:

template<class TItem, VARTYPE _vartype>
class InOutComArray<TItem, void, _vartype>
Oktalist
  • 14,336
  • 3
  • 43
  • 63
  • Ha, indeed. However, the following scenario isn't solved by this: `InOutComArray(items, length)`. Is there a way to allow this? – Daniel Hilgarth Aug 18 '14 at 13:53
  • That's not something that can be solved by specialization. A specialization doesn't introduce another way of providing template arguments. – Oktalist Aug 18 '14 at 15:47