dimanche 28 février 2016

Default constructor implicitly deleted using CRTP on VS2015 but not GCC or Clang

I'm writing a generic short vector class with a union to do type punning so I can swizzle the components. For example, if I declare Vector3 v3. I can access v3.yz as a Vector2. The following code works fine wit GCC 4.8 with -std=c++11 (or c++1y) and whatever version of Clang comes with Xcode 7.1.

However, it does not compile on Visual Studio 2015 with Update 1, using either the msvc or the Clang 3.7 toolkits:

#include <cstdint>
#include <cstdio>

template< int d, typename T, typename Derived >
class VectorBase
{
public:

    VectorBase()
    {
        for( int i = 0; i < d; ++i )
        {
            ( *this )[ i ] = T( 0 );
        }
    }

    const T& operator [] ( int i ) const
    {
        return static_cast< const Derived* >( this )->elements[ i ];
    }

    T& operator [] ( int i )
    {
        return static_cast< Derived* >( this )->elements[ i ];
    }
};

template< typename T >
class Vector2 : public VectorBase< 2, T, Vector2< T > >
{
public:

    typedef VectorBase< 2, T, Vector2 > Base;
    using Base::Base;

    union
    {
        struct
        {
            T x;
            T y;
        };
        T elements[ 2 ];
    };
};

template< typename T >
class Vector3 : public VectorBase< 3, T, Vector3< T > >
{
public:

    typedef VectorBase< 3, T, Vector3 > Base;
    using Base::Base;

    union
    {
        struct
        {
            T x;
            T y;
            T z;
        };
        struct
        {
            Vector2< T > xy;
        };
        struct
        {
            float __padding0;
            Vector2< T > yz;
        };
        T elements[ 3 ];
    };
};

int main( int argc, char* argv[] )
{
    Vector2< float > v2;
    Vector3< float > v3;

    printf( "v2 has size %zu, .x = %f, .y = %f\n", sizeof( v2 ), v2.x, v2.y );
    printf( "v3 has size %zu, .x = %f, .y = %f, .z = %f\n", sizeof( v3 ), v3.x, v3.y, v3.z );
}

VS 2015 complains with the error message:

1>  main.cpp
1>main.cpp(79,22): error : call to implicitly-deleted default constructor of 'Vector3<float>'
1>      Vector3< float > v3;
1>                       ^
1>  main.cpp(63,9) :  note: default constructor of 'Vector3<float>' is implicitly deleted because variant field '' has a non-trivial default constructor
1>          struct
1>          ^
1>  1 error generated.

It makes sense that the compiler thinks the default constructor is nontrivial and implicitly deletes it. What doesn't make sense is why inheriting the constructor with the using declaration fail, and the difference between compilers.

I can make it work by explicitly adding a Vector3 constructor that explicitly calls the VectorBase constructor in the initialization list, but that somewhat defeats the purpose of using the CRTP.

Aucun commentaire:

Enregistrer un commentaire