0
votes

I have a class Data that holds a significant amount of functions. That class may also have 0 or N additional types that are necessary in the implementation of the functions of the class:

 // in Data.h

template <typename T, typename ...Args>
class Data
{

    T f1();    
    T f2() const;    
    ... // skipped
    void f10(const T& v);

    // Pimpl
    struct Impl;
    Impl* _imp;
};

However, the implementation of some of the functions (but not all!) are completely different if sizeof...(Args) == 0 and if sizeof...(Args) > 0: Mainly, the internal private implementation class contains different types.

// in DataImpl.h

template <typename T, typename ...Args>
struct ImplementationSpecificData;

template <typename T>
struct ImplementationSpecificData<T>
{
 // Some members available when sizeof...(Args) == 0

};

template <typename T, typename ...Args>
struct ImplementationSpecificData<T, Args...>
{
 // Some members available when sizeof...(Args) > 0
};

// The pimpl
template <typename T, typename ...Args> 
struct Data<T,Args...>::Impl
{
   // Some common members 
    ...

   // Following are specific whether sizeof...(Args) is > 0 or == 0
   ImplementationSpecificData<T,Args...> specificData;
};

// In Data.cpp
#include "DataImpl.h"

Then, for functions that have an implementation that relies on specificData, I would like to specialize the functions, e.g:

template <typename T, typename ...Args> 
T
Data<T,Args...>::f1() {
    // Case where sizeof...(Args) > 0
}

// The following won't work because this is partial member template specialization

template <typename T> 
T
Data<T>::f1() {
    // Case where sizeof...(Args) == 0
}

I tried to use an extra Enable template boolean and specialize functions that needed

template <typename T, bool Enable, typename ...Args>
class Data
{
...
};

template <typename T, typename ...Args> 
T
Data<T, 
std::integral_constant<bool, sizeof...(Args) > 0>::value,
Args...>::f1() {
        // Case where sizeof...(Args) > 0
}

template <typename T, typename ...Args> 
T
Data<T, 
std::integral_constant<bool, sizeof...(Args) == 0>::value,
Args...>::f1() {
        // Case where sizeof...(Args) == 0
}

But that doesn't work either, I get the following:

nested name specifier Data<T, std::integral_constant<bool, (sizeof...(Args) > 0)>::value, Args...>:: for declaration does not refer into a class, class template or class template partial specialization

Is there an elegant way of achieving this, without polluting my API with a base class that contains pure virtual functions and derived class D1 without additional types and a derived class D2 with additional types: that would require to maintain the functions signature of the class (and there are a lot of functions) across all those 3 classes (Base, D1 and D2).

1
Data<T,Arg1,Args...>::f1() ?Yola
template <typename T, typename Arg1, typename...Args> T Data<T,Arg1,Args...>::f1() doesn't work either, I get the nested name specifier errorLex
Have you tried with std::integral_constant<bool, (sizeof...(Args) > 0)>? I mean: adding a couple of parentheses to avoid that the > in > 0 is confused with the end of integral_constant.max66
I forgot to add the parentheses, but even with them it didn't workLex

1 Answers

1
votes

Use overload/tag dispatching in your implementation:

template <typename T, typename ...Args>
T f1_impl(Data<T, Args...>&) {
    // sizeof...(Args) > 0
}

template <typename T>
T f1_impl(Data<T>&) {
    // sizeof...(Args) == 0
}

template <typename T, typename ...Args> 
T Data<T, Args...>::f1() {
    return f1_impl(*this);
}