使用/ permissive-在Visual C ++中进行编译时出现奇怪的模板错误

时间:2019-04-10 08:43:56

标签: c++ visual-studio templates

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

就像在Godbolt中一样,当我的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()保留在两个重载之间,而只是从对func()的调用中删除了Foospace名称空间限定符,那么突然也可以这样做:

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

这是怎么回事?

2 个答案:

答案 0 :(得分:4)

这里有很多C ++规则。

如Herb Sutter put it,“ 难度9/10 ”。

让我们一一考虑。

[MissingMethodException: Constructor on type '[Omitted].Module' not found.]
   System.RuntimeType.CreateInstanceImpl(BindingFlags bindingAttr, Binder binder, Object[] args, CultureInfo culture, Object[] activationAttributes, StackCrawlMark& stackMark) +1088
   System.Activator.CreateInstance(Type type, BindingFlags bindingAttr, Binder binder, Object[] args, CultureInfo culture, Object[] activationAttributes) +124
   System.Activator.CreateInstance(Type type, BindingFlags bindingAttr, Binder binder, Object[] args, CultureInfo culture) +20
   System.Web.HttpRuntime.CreateNonPublicInstance(Type type, Object[] args) +60
   System.Web.HttpRuntime.CreateNonPublicInstanceByWebObjectActivator(Type type) +59
   System.Web.HttpApplication.BuildIntegratedModuleCollection(List`1 moduleList) +167
   System.Web.HttpApplication.GetModuleCollection(IntPtr appContext) +1068
   System.Web.HttpApplication.RegisterEventSubscriptionsWithIIS(IntPtr appContext, HttpContext context, MethodInfo[] handlers) +82
   System.Web.HttpApplication.InitSpecial(HttpApplicationState state, MethodInfo[] handlers, IntPtr appContext, HttpContext context) +173
   System.Web.HttpApplicationFactory.GetSpecialApplicationInstance(IntPtr appContext, HttpContext context) +218
   System.Web.Hosting.PipelineRuntime.InitializeApplication(IntPtr appContext) +296

[HttpException (0x80004005): Constructor on type '[Omitted].Module' not found.]
   System.Web.HttpRuntime.FirstRequestInit(HttpContext context) +10075108
   System.Web.HttpRuntime.EnsureFirstRequestInit(HttpContext context) +95
   System.Web.HttpRuntime.ProcessRequestNotificationPrivate(IIS7WorkerRequest wr, HttpContext context) +254

在这里,我们在模板中进行了限定查找。查找名称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>(); } } 并“绑定” at the point of template definition。那时仅知道Foospace::func,因此对func(A*)的后续调用失败。编译器拒绝代码是正确的。请注意,此wasn't implemented在MSVC中直到最近。

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*)

此处查找未找到任何内容。该代码应无法编译。由于某种原因,MSVC会对其进行编译,这表明它是错误或功能/扩展。 GCC和clang reject it

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

此处的查询不合格。 ADL规则适用,因此在模板定义时无法确定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>(); } } 是否解析,因此阶段1查找总是允许其进行。当两个声明都已知时,将在at the point of template instantiation中查找名称。该代码可以很好地编译。

答案 1 :(得分:1)

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

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

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

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

>

设置此选项后,代码将编译。参见 godbolt demo.

相关的错误报告:

  1. Implement correct two-phase lookup for C++ templates

  2. Using /permissive- in a C++/CLI project triggers two-phase name lookup warnings