Home > Blockchain >  MSVC insists on using inaccessible member from private-inherited base class, although it was re-defi
MSVC insists on using inaccessible member from private-inherited base class, although it was re-defi

Time:10-29

Consider this code (godbolt example):

#include <cstddef>

template< std::size_t... sizes >
class Impl
{
public:
    static constexpr std::size_t
    getDimension()
    {
        return sizeof...( sizes );
    }
};

template< std::size_t... sizes >
class Wrapper : private Impl< sizes... >
{
    using BaseType = Impl< sizes... >;

public:
    //using BaseType::getDimension;
    static constexpr std::size_t
    getDimension()
    {
        return BaseType::getDimension();
    }
};

template< typename A, typename B >
class Combine : public B
{
public:
    static_assert( A::getDimension() > 0 );
    static_assert( B::getDimension() > 0 );
    static_assert( A::getDimension() == B::getDimension(),
                   "dimensions of A and b do not match" );
};

int main()
{
    Combine< Impl< 0 >, Wrapper< 0 > > a;
}

Trying to compile with MSVC (v19 with /std:c 17 /permissive-) leads to this error:

example.cpp
<source>(32): error C2247: 'Impl<0>::getDimension' not accessible because 'Wrapper<0>' uses 'private' to inherit from 'Impl<0>'
<source>(8): note: see declaration of 'Impl<0>::getDimension'
<source>(40): note: see declaration of 'Wrapper<0>'
<source>(40): note: see declaration of 'Impl<0>'
<source>(32): error C2248: 'Impl<0>::getDimension': cannot access private member declared in class 'Wrapper<0>'
<source>(8): note: see declaration of 'Impl<0>::getDimension'
<source>(40): note: see declaration of 'Wrapper<0>'
<source>(40): note: see reference to class template instantiation 'Combine<Impl<0>,Wrapper<0>>' being compiled
<source>(34): error C2247: 'Impl<0>::getDimension' not accessible because 'Wrapper<0>' uses 'private' to inherit from 'Impl<0>'
<source>(8): note: see declaration of 'Impl<0>::getDimension'
<source>(40): note: see declaration of 'Wrapper<0>'
<source>(40): note: see declaration of 'Impl<0>'
<source>(34): error C2248: 'Impl<0>::getDimension': cannot access private member declared in class 'Wrapper<0>'
<source>(8): note: see declaration of 'Impl<0>::getDimension'
<source>(40): note: see declaration of 'Wrapper<0>'
Compiler returned: 2

However, the code compiles fine with state-of-the-art compilers like GCC and Clang, so I assume the code is correct. Is there a workaround for MSVC? Note that the private inheritance has a purpose in the real world.

CodePudding user response:

An ugly workaround exists. First, you might define an external template function for delegating to the proper class:

template<typename T>
constexpr std::size_t getDimension()
{
    return T::getDimension();
}

This is so that MSVC's name resolution won't try to search in the private base classes. Then, you might write:

template< typename A, typename B >
class Combine : public B
{
public:
    static_assert( ::getDimension<A>() == ::getDimension<B>(),
                   "dimensions of A and b do not match" );
};

The :: is not mandatory, it's only for cases where getDimension() would also take template arguments as a member function.

This hack uses the fact that it's a static member function; if you have a non-static member function, you need to pass this as well.

Example: https://godbolt.org/z/4fax9dGvd


An alternative solution is to wrap the call to an inner struct:

template< typename A, typename B >
class Combine : public B
{
public:
    static struct {
        static_assert( A::getDimension() == B::getDimension(),
                       "dimensions of A and b do not match" );
    } verify;
};

This does not require a global template function to delegate, but neither is ran direcly inside your class (which might or might not be a problem).

  • Related