首页 > 解决方案 > 使用 /permissive- 在 Visual C++ 中编译时出现奇怪的模板错误

问题描述

我正在尝试使用 /permissive- 在 VS2019 中编译一些代码,其中涉及模板和重载,并且正在发生奇怪的事情。(https://godbolt.org/z/fBbQu6

就像在上帝螺栓中一样,当我的 templateFunc() 在两个重载之间声明时,如下所示:

namespace Foospace {
    class A;
    void func(A*) {};

    template<class T> void templateFunc() { Foospace::func((T*)0); }

    class B;
    void func(B*) {};

    void func() { Foospace::templateFunc<B>(); }
}

我明白了error C2664: 'void Foospace::func(Foospace::A *)': cannot convert argument 1 from 'T *' to 'Foospace::A *'

如果我将 templateFunc() 移到重载之下,它显然可以工作:

namespace Foospace {
    class A;
    void func(A*) {};

    class B;
    void func(B*) {};

    template<class T> void templateFunc() { Foospace::func((T*)0); }

    void func() { Foospace::templateFunc<B>(); }
}

如果我在两个也有效的重载之前移动 templateFunc() :

namespace Foospace {
    template<class T> void templateFunc() { Foospace::func((T*)0); }

    class A;
    void func(A*) {};

    class B;
    void func(B*) {};

    void func() { Foospace::templateFunc<B>(); }
}

而且,如果我将 templateFunc() 保留在两个重载之间,但只是Foospace从对 func() 的调用中删除了命名空间限定符,那么突然之间也可以使用:

namespace Foospace {
    class A;
    void func(A*) {};

    template<class T> void templateFunc() { func((T*)0); }

    class B;
    void func(B*) {};

    void func() { Foospace::templateFunc<B>(); }
}

这里发生了什么?

标签: c++visual-studiotemplates

解决方案


这里有很多 C++ 规则在起作用。

正如 Herb Sutter所说,“难度 9/10 ”。

让我们一一考虑案例。

namespace Foospace {
    class A;
    void func(A*) {};

    template<class T> void templateFunc() { Foospace::func((T*)0); }

    class B;
    void func(B*) {};

    void func() { Foospace::templateFunc<B>(); }
}

在这里,我们在模板中进行了限定查找。在模板定义处Foospace::func查找并“绑定”名称。只有在那个时候是已知的,因此随后的调用失败。编译器拒绝代码是正确的。请注意,这直到最近才在 MSVC中实现。func(A*)func(B*)

namespace Foospace {
    class A;
    void func(A*) {};

    class B;
    void func(B*) {};

    template<class T> void templateFunc() { Foospace::func((T*)0); }

    void func() { Foospace::templateFunc<B>(); }
}

同样的故事,只有在重载集 func(A*)的查找结果,func(B*)。所以两个电话都成功了。

namespace Foospace {
    template<class T> void templateFunc() { Foospace::func((T*)0); }

    class A;
    void func(A*) {};

    class B;
    void func(B*) {};

    void func() { Foospace::templateFunc<B>(); }
}

在这里查找什么也没找到。代码应该无法编译。出于某种原因,MSVC 编译它,这表明它要么是错误,要么是功能/扩展。GCC 和 clang拒绝它

namespace Foospace {
    class A;
    void func(A*) {};

    template<class T> void templateFunc() { func((T*)0); }

    class B;
    void func(B*) {};

    void func() { Foospace::templateFunc<B>(); }
}

不合格的查找在这里。ADL 规则适用,因此在模板定义时不可能说是否func(T*)解决了,因此第 1 阶段查找总是让它继续进行。当两个声明都知道时,在模板实例化点查找名称。代码编译得很好。


推荐阅读