Bizarre template errors when compiling in Visual C++ with /permissive-

Ang*_*ham 7 c++ templates visual-studio

I'm trying to compile some code in VS2019 with /permissive- that involves both templates and overloading and wierd things are happening. (https://godbolt.org/z/fBbQu6)

As in the godbolt, when my templateFunc() is declared between two overloads like so:

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>(); }
}
Run Code Online (Sandbox Code Playgroud)

I get error C2664: 'void Foospace::func(Foospace::A *)': cannot convert argument 1 from 'T *' to 'Foospace::A *'

If I move the templateFunc() below the overloads, it obviously works:

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>(); }
}
Run Code Online (Sandbox Code Playgroud)

And if I move templateFunc() before both overloads that also works:

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>(); }
}
Run Code Online (Sandbox Code Playgroud)

And if I keep templateFunc() between the two overloads but simply remove the Foospace namespace qualifier from the call to func() then suddenly that also works too:

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>(); }
}
Run Code Online (Sandbox Code Playgroud)

What is going on here?

rus*_*tyx 5

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

正如赫伯·萨特所说,“难度 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>(); }
}
Run Code Online (Sandbox Code Playgroud)

在这里,我们在模板中进行了限定查找在模板定义点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>(); }
}
Run Code Online (Sandbox Code Playgroud)

同样的故事,只有在重载集 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>(); }
}
Run Code Online (Sandbox Code Playgroud)

在这里查找一无所获。代码应该无法编译。出于某种原因,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>(); }
}
Run Code Online (Sandbox Code Playgroud)

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


P.W*_*P.W 1

MSVC 和两阶段查找存在一些问题。

在 Visual Studio 2017 版本 15.3 及更高版本中,默认情况下,编译器使用两阶段名称查找进行模板名称解析。

/permissive-选项隐式设置一致的两阶段查找编译器行为,但可以使用 覆盖它/Zc:twoPhase-

如果指定了该选项/Zc:twoPhase-,编译器将恢复到以前的不合格类模板和函数模板名称解析和替换行为。

设置此选项后,代码即可编译。请参阅godbolt 演示。

相关错误报告:

  1. 为 C++ 模板实现正确的两阶段查找

  2. 在 C++/CLI 项目中使用 /permissive- 会触发两阶段名称查找警告