Home > Back-end >  class template specialization based on literal type using enable_if
class template specialization based on literal type using enable_if

Time:07-22

I am trying to create a Vector class, templated on a type T and number of elements NUM_VALUES . I want the class to use a std::vector to store its elements when NUM_VALUES > 16384 and a std::array<T, NUM_VALUES> to stores its elements when NUM_VALUES <= 16384. At first I tried doing the following:

vector.h:

#include <array>
#include <vector>

#include <cstddef>

template <typename T, std::size_t NUM_VALUES, typename Elements = std::vector<T>>
class Vector
{
public:
    constexpr const char* hello()
    {
        return "hello";
    }
};

template <typename T, std::size_t NUM_VALUES>
class Vector<T, NUM_VALUES, typename std::enable_if<NUM_VALUES <= 16384, std::array<T, NUM_VALUES>>::type>
{
public:
    constexpr const char* hello()
    {
        return "Hello";
    }
};

main.cpp

#include "vector.h"

#include <iostream>

int main()
{
    Vector<int, 10> vec1;
    Vector<int, 1000000> vec2;
    std::cout << vec1.hello() << ", " << vec2.hello();
    return 0;
}

However this always selects the primary template, so I get "hello", "hello" instead of "hello", "Hello". I tried fixing this by changing vector.h to:

#include <array>
#include <vector>

#include <cstddef>

template <typename T, std::size_t NUM_VALUES, typename Elements = typename std::enable_if<16384 < NUM_VALUES, std::vector<T>>::type>
class Vector
{
public:
    constexpr const char* hello()
    {
        return "hello";
    }
};

template <typename T, std::size_t NUM_VALUES>
class Vector<T, NUM_VALUES, typename std::enable_if<NUM_VALUES <= 16384, std::array<T, NUM_VALUES>>::type>
{
public:
    constexpr const char* hello()
    {
        return "Hello";
    }
};

but got the error:

\MyVector\main.cpp(7,16): error C2146: syntax error: missing '>' before identifier 'type'
\MyVector\MyVector\main.cpp(7,2): error C2976: 'Vector': too few template arguments
\MyVector\MyVector\vector.h(9): message : see declaration of 'Vector'
\MyVector\MyVector\main.cpp(7,18): error C2146: syntax error: missing '>' before identifier 'type'
\MyVector\MyVector\main.cpp(7): error C2641: cannot deduce template arguments for 'Vector'
\MyVector\MyVector\main.cpp(7): error C2783: 'Vector<T,NUM_VALUES,Elements> Vector(void)': could not deduce template argument for 'T'
\MyVector\MyVector\vector.h(9): message : see declaration of 'Vector'
\MyVector\MyVector\main.cpp(7): error C2783: 'Vector<T,NUM_VALUES,Elements> Vector(void)': could not deduce template argument for 'NUM_VALUES'
\MyVector\MyVector\vector.h(9): message : see declaration of 'Vector'
\MyVector\MyVector\main.cpp(7,22): error C2780: 'Vector<T,NUM_VALUES,Elements> Vector(Vector<T,NUM_VALUES,Elements>)': expects 1 arguments - 0 provided
\MyVector\MyVector\vector.h(9): message : see declaration of 'Vector

I am using visual studio 2022 to compile the code.

CodePudding user response:

Type SFINAE is not part of the C standard, but you could easily accomplish the desired results in a different manner.

Define seperate templates for both alternatives. Then define an alias template Vector that uses std::conditional_t to detemine the type to use:

constexpr std::size_t SmallVectorMaxSize = 16384;

template <typename T, std::size_t NUM_VALUES>
class BigVector
{
public:
    static_assert(NUM_VALUES > SmallVectorMaxSize);

    using Elements = std::vector<T>;

    constexpr const char* hello()
    {
        return "hello";
    }
};

template <typename T, std::size_t NUM_VALUES>
class SmallVector
{
public:
    static_assert(NUM_VALUES <= SmallVectorMaxSize);

    using Elements = std::array<T, NUM_VALUES>;

    constexpr const char* hello()
    {
        return "Hello";
    }
};

template<class T, size_t N>
using Vector = std::conditional_t<N <= SmallVectorMaxSize, SmallVector<T, N>, BigVector<T, N>>;

int main()
{
    Vector<int, 10> vec1;
    Vector<int, 1000000> vec2;
    std::cout << vec1.hello() << ", " << vec2.hello();
    return 0;
}
  • Related