StefanKssmr
StefanKssmr

Reputation: 1226

C++20 Concepts: out-of-line-definition fails with MSVC but not in GCC or clang

Consider this little code snippet

namespace nsp
{
    template<typename T>
    concept Addable= requires(const T& a,const T& b)
    {
        {a + b} -> std::convertible_to<T>;
    };

    template<Addable T>
    struct C
    {
        C();
    };
}

template<nsp::Addable T>
nsp::C<T>::C() {};

As shown here GCC (10.2) and clang (11.0) accept the code while MSVC (x86 19.28) rejects it with the error message:

error C3855: 'nsp::C<T>': template parameter 'T' is incompatible with the declaration. 

Is this a MSVC bug or are GCC and clang wrong accepting it? Or alternatively, did I something stupid? If I move the out-of-line definition into the namespace nsp it seems to work for MSVC, as well. See this example.

Upvotes: 2

Views: 333

Answers (1)

Bitwize
Bitwize

Reputation: 11230

This behavior is an observable deviation in MSVC that has been seen usually in the context of templates and SFINAE. MSVC tends to have difficulty with out-of-line definitions of templates that have qualifications when the declarations are not qualified (from being in the same namespace). I've often encountered this when dealing with forms of SFINAE, and it appears this must be also occurring with concepts now too.

For example, MSVC rejects the valid code:

namespace nsp {

  template <typename T>
  using is_convertible_to_bool = std::is_convertible<T, bool>;

  template <typename T, std::enable_if_t<is_convertible_to_bool<T>::value,int> = 0>
  void example(const T& x);

} // namespace nsp

template <typename T, std::enable_if_t<nsp::is_convertible_to_bool<T>::value,int>>
void nsp::example(const T& x)
{

}

Live Example

However, MSVC will accept the same code, provided you add qualifications on the is_convertible_to_bool from namespace nsp:

  template <typename T, std::enable_if_t<nsp::is_convertible_to_bool<T>::value,int> = 0>
  //                                     ^~~~~ fixes it
  void example(const T& x);

Live example

Similarly, your code sample actually works if you change the definition of struct C to include the fully qualified concept name:

    template<nsp::Addable T>
    //       ^~~~~
    //       Qualifying here fixes the failure in MSVC
    struct C
    {
        C();
    };

Live Example


I don't have time to check the standard for lookup rules for which compiler is correct (will do this later if no other answer appears), but my expectation is actually that MSVC is providing incorrect behavior. The basic name lookup should select the same type in both definitions, and thus the code should be well-formed.

Upvotes: 2

Related Questions