简体   繁体   中英

Nested template C++

I have a template class of the form:

template<typename ContainerType>
class ConfIntParamStat {
  public:
    typedef typename ContainerType::Type Type;
...
  private:
    void sample(int iteration) {...}
}

I would like to create a specific version of the function sample for the case when ContainerType is a Vector. Where Vector itself is a template class, but I do not know which type of values this Vector holds.

My intuition was to create this in the header file:

template<typename Type>
ConfIntParamStat<Vector<Type> >::sample(int iteration) {
...
}

But it does not compile, and the error from clang is:

error: nested name specifier 'ConfIntParamStat<Vector<Type> >::' for declaration does not refer into a class, class template or class template partial specialization

Is it possible using another syntax ?

You could make use of the overloading mechanism and tag dispatch:

#include <vector>

template <class T>
struct Tag { };

template<typename ContainerType>
class ConfIntParamStat {
  public:
    typedef typename ContainerType::value_type Type;
//...
//  private:
    void sample(int iteration) {
       sample_impl(Tag<ContainerType>(), iteration);
    }

    template <class T>
    void sample_impl(Tag<std::vector<T> >, int iteration) {
       //if vector
    }

    template <class T>
    void sample_impl(Tag<T>, int iteration) {
       //if not a vector
    }
};

int main() {
   ConfIntParamStat<std::vector<int> > cips;
   cips.sample(1);
}

As skypjack mentioned this approach has a little draw when using const. If you are not using c++11 (I suspect you dont because you use > > syntax for nested templates) you could workaround this as follows:

#include <iostream>
#include <vector>

template <class T>
struct Tag { };

template <class T>
struct Decay {
   typedef T Type;
};

template <class T>
struct Decay<const T> {
   typedef T Type;
};

template<typename ContainerType>
class ConfIntParamStat {
  public:
    typedef typename ContainerType::value_type Type;
//...
//  private:
    void sample(int iteration) {
       sample_impl(Tag<typename Decay<ContainerType>::Type>(), iteration);
    }

    template <class T>
    void sample_impl(Tag<std::vector<T> >, int iteration) {
       std::cout << "vector specialization" << std::endl;
    }

    template <class T>
    void sample_impl(Tag<T>, int iteration) {
       std::cout << "general" << std::endl;
    }
};

int main() {
   ConfIntParamStat<const std::vector<int> > cips;
   cips.sample(1);
}

If you didnt want to specialize the template and were looking for a member only specialization try the following

#include <iostream>
#include <vector>
using namespace std;

template <typename ContainerType>
class Something {
  public:

    void do_something(int);

    template <typename Which>
    struct do_something_implementation {
        void operator()() {
            cout << "general implementation" << endl;
        }
    };
    template <typename Which>
    struct do_something_implementation<vector<Which>> {
        void operator()() {
            cout << "specialized implementation for vectors" << endl;
        }
    };
};

template <typename ContainerType>
void Something<ContainerType>::do_something(int) {
    do_something_implementation<ContainerType>{}();
}

int main() {
    Something<double> something;
    something.do_something(1);

    return 0;
}

If your intent is to specialize a function, I would just overload the function like so

#include <iostream>
#include <vector>
using namespace std;

template <typename ContainerType>
class Something {
  public:

    void do_something(int);

    template <typename Type>
    void do_something(const vector<Type>&);
};

template <typename ContainerType>
void Something<ContainerType>::do_something(int) {
    cout << "Called the general method for do_something" << endl;
}

template <typename ContainerType>
template <typename Type>
void Something<ContainerType>::do_something(const vector<Type>&) {
    cout << "Called the specialised method" << endl;
}

int main() {
    vector<int> vec{1, 2, 3};
    Something<double> something;
    something.do_something(1);
    something.do_something(vec);

    return 0;
}

This is mostly why full/explicit function template specializations are not required. Overloading allows for almost the same effects!

Note This is a great article related to your question! http://www.gotw.ca/publications/mill17.htm

Another way to approach this is composition.

The act of adding a sample can be thought of as a component of the implementation of the class. If we remove the implementation of adding a sample into this template class, we can then partially specialise only this discrete component.

For example:

#include <vector>

// 
// default implementation of the sample component
//
template<class Outer>
struct implements_sample
{
  using sample_implementation = implements_sample;

  // implements one function
  void sample(int iteration) {
    // default actions
    auto self = static_cast<Outer*>(this);
    // do something with self
    // e.g. self->_samples.insert(self->_samples.end(), iteration);
  }
};

// refactor the container to be composed of component(s)
template<typename ContainerType>
class ConfIntParamStat 
: private implements_sample<ConfIntParamStat<ContainerType>>
{
    using this_class = ConfIntParamStat<ContainerType>;
  public:

    // I have added a public interface    
    void activate_sample(int i) { sample(i); }

    // here we give the components rights over this class        
  private:
    friend implements_sample<this_class>;
    using this_class::sample_implementation::sample;

    ContainerType _samples;
};

//
// now specialise the sample function component for std::vector
//
template<class T, class A>
  struct implements_sample<ConfIntParamStat<std::vector<T, A>>>
  {
  using sample_implementation = implements_sample;
  void sample(int iteration) {
    auto self = static_cast<ConfIntParamStat<std::vector<T, A>>*>(this);
    // do something with self
    self->_samples.push_back(iteration);
  }
};



int main()
{
  ConfIntParamStat< std::vector<int> > cip;
  cip.activate_sample(1);
  cip.activate_sample(2);

}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM