Search code examples
c++c++11template-meta-programmingsfinaeenable-if

Enable a struct if enable_if determines that T is a container?


I am trying to build a templated struct that will only take containers for T. I found this post that showed how to determine whether the passed in value is a container or not. So I decided to go ahead and try to use that for my program as I do not want the user to create a struct of integers, floats, or doubles.

Here is code that I have written:

template<typename T> 
struct is_container : std::integral_constant<bool, has_const_iterator<T>::value && has_begin_end<T>::beg_value && has_begin_end<T>::end_value> { };

template<typename T, typename Enable = void>
struct Cont;

template <typename T>
struct Cont<T, typename std::enable_if<is_container<T>::value>>
{
  Cont(const std::string &n) : name(n) {}
  std::string name;
};

However, when I try to write to main:

int main()
{
  Cont<std::vector<int>> myContainer("Vector");
}

I get a compiler error: Cont<std::vector<int> > myContainer has initializer but incomplete type. I'm kind of stuck on where to go with this, because if I remove the std::enable_if from the template parameter, it compiles just fine. Which leads me to believe that I am doing something wrong with std::enable_if or I am missing something rather simple.

What I am trying to achieve is the following:

int main()
{
  Cont<std::vector<int>> myContainer("Vector"); //happily compiles
  Cont<int> badContainer("Bad"); // will not compile
}

How can I achieve this?


Solution

  • Which leads me to believe that I am doing something wrong with std::enable_if or I am missing something rather simple.

    Exactly.

    You have forgotten a ::type

    template <typename T>  // add this ---------------------------vvvvvv
    struct Cont<T, typename std::enable_if<is_container<T>::value>::type>
    {
      Cont(const std::string &n) : name(n) {}
      std::string name;
    };
    

    Starting from C++14, you can also use std::enable_if_t (so you can remove the ::type and the preceding typename)

    template <typename T>
    struct Cont<T, std::enable_if_t<is_container<T>::value>>
    {
      Cont(const std::string &n) : name(n) {}
      std::string name;
    };