MadScientist
MadScientist

Reputation: 100966

Initialze a member of a C++ templated class only for specific type

Suppose I have a templated class like this:

template <typename C>
class ValArray
{
    ValArray() = default;
    ValArray(const ValArray&) = delete;

    C& operator[](size_t pos) { return _values[pos]; }
  ...
private:
    std::array<C, ARRAY_SIZE> _values;
};

This is instantiated with lots of different types. What I would like to do is initialize _values only for type bool but not for other types. So, if I create ValArray<bool> val then I want all elements to be false, for example:

std::array<bool, ARRAY_SIZE> _values = {false};

But for any other type, I don't want to initialize _values at all.

Is there some template magic I haven't been able to find that will allow this? I'm hoping to not have to create a separate class for this, as this would cascade to many other places where I'd have to create specializations.

Upvotes: 1

Views: 106

Answers (2)

Swift - Friday Pie
Swift - Friday Pie

Reputation: 14673

Among ways HOW this can be done you can simply add a specialization for a class member after declaration you already have for this case, something like this:

template<> 
constexpr ValArray<bool>::ValArray() noexcept : _values({false}) {};

Because ctor was defaulted explicitly that should be moved and exception specifier should be consistent across all constructors:

template <typename C>
class ValArray
{
public:
    ValArray() noexcept;
    ValArray(const ValArray&) = delete;

    C& operator[](size_t pos) { return _values[pos]; }

private:
    std::array<C, ARRAY_SIZE> _values;
};

template<typename C>  ValArray<C>::ValArray() noexcept = default;
template<>  ValArray<bool>::ValArray() noexcept : _values({false}) {};

Similar constructs for specialization are available in very early version of C++, no SFINAE or if constexpr is required in this case.

Upvotes: 2

Remy Lebeau
Remy Lebeau

Reputation: 597215

In C++17 and later, you can use if constexpr inside the constructor to assign the array elements, eg:

template <typename C>
class ValArray
{
public:
    ValArray() {
        if constexpr (std::is_same_v<C, bool>) {
            _values.fill(false);
        }
    }

    ...

private:
    std::array<C, ARRAY_SIZE> _values;
};

Online Demo

If C is bool, the constructor will be compiled as:

ValArray() { _values.fill(false); }

Otherwise it will be compiled as:

ValArray() {}

Prior to C++17, you can accomplish the same thing using SFINAE via std::enable_if, eg:

template <typename C>
class ValArray
{
public:
    template<typename T = C, typename std::enable_if<std::is_same<T, bool>::value, int>::type = 0>
    ValArray() { _values.fill(false); }

    template<typename T = C, typename std::enable_if<!std::is_same<T, bool>::value, int>::type = 0>
    ValArray() { }
    
    // ...

private:
    std::array<C, ARRAY_SIZE> _values;
};

Online Demo

Upvotes: 3

Related Questions