c++visual-c++c++20c++-conceptsrequires-clause

The concept `std::movable` evaluated to false for an empty class in Visual C++


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?


Solution

  • (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:

    1. Moving A's definition above B's solves the problem for all 3 compilers.

      See demo1.

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