My C++20 program works fine when compiled with GCC or Clang. But when I build it in Visual Studio, an unexpected compilation error occurs.
Maximally abbreviated example:
#include <concepts>
template<class T>
struct MyContainer {
MyContainer() = default;
explicit MyContainer( std::size_t ) requires( std::default_initializable<T> );
void resize( std::size_t ) requires( std::movable<T> && std::default_initializable<T> ) {}
};
struct A;
struct B {
MyContainer<A> c;
};
struct A{};
int main() {
B b;
b.c.resize( 42 );
}
MSVC error is
<source>(18): error C7500: 'resize': no function satisfied its constraints
<source>(7): note: could be 'void MyContainer<A>::resize(size_t)'
<source>(7): note: the 'MyContainer<A>::resize' constraint was not satisfied
<source>(7): note: the concept 'std::movable<A>' evaluated to false
Online demo: https://gcc.godbolt.org/z/cs5Pa7xzv
Does the program contain something illegal or the error is incorrect?
(This was initialy posted as a speculative comment, but since it got a few positive responses I though it might as well be useful as an answer.)
The problem is not that MSVC is evaluating the std::movable
concept to false
for an empty class (A
) in general.
Looks like it is cause by the fact that A
is only forward declared before it is used in MyContainer<A> c;
. When the MSVC compiler parses MyContainer<A> c;
, A
is still incomplete and therefore the compiler cannot verify that it respects the concept.
I am not sure whether MSVC is correct here according to the standard or gcc+clang.
Possible workarounds:
Moving A
's definition above B
's solves the problem for all 3 compilers.
See demo1.
Alteratively (as commented above) if you must define A
after B
, you can add a template parameter and make A
a default for it (you can also add a static_assert
to make sure no other class is used). This also works on all 3 compilers.
See demo2.