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).