C++20 概念:外线定义在 MSVC 中失败,但在 GCC 或 clang 中没有

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

考虑这个小代码片段

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() {};

如图所示here GCC (10.2) 和 clang (11.0) 接受代码,而 MSVC (x86 19.28) 拒绝它并显示错误消息:

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

这是 MSVC 错误还是 GCC 和 clang 接受错误?或者,我是不是做了什么蠢事?如果我将外联定义移动到命名空间 nsp 中,它似乎也适用于 MSVC。看到这个 example.

此行为是 MSVC 中的一个可观察到的偏差,通常在模板和 SFINAE 的上下文中看到。当声明不合格时(由于位于同一名称空间中),MSVC 往往难以处理具有限定条件的模板的外联定义。我在处理 SFINAE 的形式时经常遇到这种情况,而且现在 concepts 似乎也一定会发生这种情况。

例如,MSVC 拒绝有效代码:

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

但是,MSVC 将接受相同的代码,前提是您在 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

同样,如果您更改 struct C 的定义以包含完全限定的概念名称,您的代码示例实际上可以工作:

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

Live Example


我没有时间检查编译器正确的查找规则标准(如果没有其他答案出现,稍后会这样做),但我的期望实际上是 MSVC 提供 不正确 行为。基本名称查找在两个定义中应该 select 相同的类型,因此代码 应该 格式正确。