Search code examples
c++templatesc++11template-meta-programmingenable-if

std::enable_if not working as expected in Visual Studio 2013


I was writing a small library in C++ where I only want the template to be instantiated if it is templated on an arithmetic type and I found the following issue:

If I have the following definition of Foo:

template<typename T, typename Enable = void>
class Foo;

template<typename T>
class Foo<T, std::enable_if<std::is_arithmetic<T>::value>::type> {
    Foo() = default;
    Foo( const Foo& ) = default;
    ~Foo() = default;

    template<typename U>
    Foo( std::initializer_list<U> list )
    {
        static_assert(std::is_convertible<U, T>::value, "Must use an initializer list with type convertible to T");

        for( std::size_t s = 0; s < 10; ++s )
        {
            tArray[s] = static_cast<U>(list[s]);
        }
    }

private:
    T       tArray[10];
};

And I try to initialize it as follows:

int main()
{
    Foo<int> test{ {1, 2, 3, 4, 5, 6, 7, 8, 9, 10} };

    return 0;
}

I get the following error:

Foo<T, std::enable_if<std::is_arithmetic<T>::value>::type>::Foo( std::initializer_list<U> ) [with T=int, U=int] is inaccessible

I'm new to using std::enable_if in TMP, but according to cppreference.com it appears that this should work. What is it that I'm missing here, or is this a bug with VS2013?


Solution

  • Your initializer is declared as private. Declare it as public. And don't forget to add typename when using std::enable_if<>::type.

    Edit: There is no subscript overloading in std::initializer_list.

    template<typename T, typename Enable = void>
    class Foo;
    
    template<typename T>
    class Foo<T, typename std::enable_if<std::is_arithmetic<T>::value>::type> {
    
    public:
        Foo() = default;
        Foo( const Foo& ) = default;
        ~Foo() = default;
    
        template<typename U>
        Foo( std::initializer_list<U> list )
        {
            static_assert(std::is_convertible<U, T>::value, "Must use an initializer list with type convertible to T");
    
            for( std::size_t s = 0; s < 10; ++s )
            {
                // ERROR
                // tArray[s] = static_cast<U>(list[s]);
            }
        }
    
    private:
        T       tArray[10];
    };