c++ - 使用 /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++ 规则在起作用。
正如 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 阶段查找总是让它继续进行。当两个声明都知道时,在模板实例化点查找名称。代码编译得很好。
推荐阅读
- sql - SQL Server 使列数据不可删除
- xslt - XSL - 相交两个集合并返回找到的公共值
- c++ - 使用 CGAL 将多面体投影到 xy 平面
- java - 在 Eclipse(Indigo)中构建声纳时出错 无法执行目标 org.sonarsource.scanner.maven:sonar-maven-plugin:3.4.0.905:sonar
- laravel - 对要使用的关系类型感到困惑
- c - 在C中更改子进程中的当前工作目录
- jquery - Django,访问从 JQuery AJAX 发送的 html 表单字段值
- python - 在虚拟环境中运行 Python 时忽略 PATH
- ubuntu - 如何设置密码以便我可以连接到 Google Cloud 实例上的 FTP
- postgresql - 将 postgresql 模式传递给 alembic 迁移