C++ 奇怪的 templates/namespaces 行为
C++ weird templates/namespaces behavior
我在编译代码时遇到问题,因为它无法在模板上找到匹配的函数。我已经将问题缩小到这个例子:
namespace cv
{
class FileNode
{ };
template<typename _Tp> static inline void operator >> (const FileNode& n, _Tp& value)
{
read(n, value, _Tp());
}
static inline void read(const FileNode& node, bool& value, bool default_value)
{ }
}
class K
{ };
namespace D
{
class A
{ };
}
template<class X>
static void read(const cv::FileNode& node, X& x, const X& default_value)
{
return;
}
using namespace D;
class B
{
void read(const cv::FileNode& fn)
{
A a;
fn >> a;
}
};
int main(int argc, char* argv[]) { }
在 Gcc 9.10 上出现以下错误:
invalid initialization of reference of type 'bool&' from expression of type 'D::A' { read(n, value, _Tp()); }
2019 年 Visual Studio:
Error C2664 'void cv::read(const cv::FileNode &,bool &,bool)': cannot convert argument 2 from '_Tp' to 'bool &'
我发现以下任何更改都会使代码编译:
class A
-> class A : public K
- 删除
read
bool
的专业化
- 删除
cv
命名空间
- 将
read
模板移入命名空间 D
不幸的是,none 之前的修复适用于我原来的问题,但我仍然没有真正理解为什么它无法找到 read
模板。
ADL 反击:
在
template<typename _Tp> static inline void operator >> (const FileNode& n, _Tp& value)
{
read(n, value, _Tp());
}
read
未声明,因此只能通过 ADL
找到
因此它将在与 FileNode
关联的命名空间中搜索(因此 cv
),以及与 _Tp
关联的命名空间。
当 _Tp
为 D::A
时,它将是命名空间 D
。
read
唯一可能的重载是 cv::read
,它需要 bool
.
将 read<T>
的声明移至上方 cv::operator >>
解决问题,也将考虑使用 ADL。
通常,我会将 类 和命名空间分隔到不同的 .hpp 文件中,这将自动强制您创建函数:
template<typename _Tp> static inline void operator >> (const FileNode& n, _Tp& value)
在使用它的命名空间之前声明(如cv
)。
我认为避免此类问题的最好方法是清理代码,并使其尽可能分离和独立。这样,无论何时你想在另一个命名空间中使用这个独特的 read
函数,你都不需要将新的命名空间放在这个文件中,或者包括当前在这个文件中的所有命名空间。
我在编译代码时遇到问题,因为它无法在模板上找到匹配的函数。我已经将问题缩小到这个例子:
namespace cv
{
class FileNode
{ };
template<typename _Tp> static inline void operator >> (const FileNode& n, _Tp& value)
{
read(n, value, _Tp());
}
static inline void read(const FileNode& node, bool& value, bool default_value)
{ }
}
class K
{ };
namespace D
{
class A
{ };
}
template<class X>
static void read(const cv::FileNode& node, X& x, const X& default_value)
{
return;
}
using namespace D;
class B
{
void read(const cv::FileNode& fn)
{
A a;
fn >> a;
}
};
int main(int argc, char* argv[]) { }
在 Gcc 9.10 上出现以下错误:
invalid initialization of reference of type 'bool&' from expression of type 'D::A' { read(n, value, _Tp()); }
2019 年 Visual Studio:
Error C2664 'void cv::read(const cv::FileNode &,bool &,bool)': cannot convert argument 2 from '_Tp' to 'bool &'
我发现以下任何更改都会使代码编译:
class A
->class A : public K
- 删除
read
bool
的专业化
- 删除
cv
命名空间 - 将
read
模板移入命名空间D
不幸的是,none 之前的修复适用于我原来的问题,但我仍然没有真正理解为什么它无法找到 read
模板。
ADL 反击:
在
template<typename _Tp> static inline void operator >> (const FileNode& n, _Tp& value)
{
read(n, value, _Tp());
}
read
未声明,因此只能通过 ADL
因此它将在与 FileNode
关联的命名空间中搜索(因此 cv
),以及与 _Tp
关联的命名空间。
当 _Tp
为 D::A
时,它将是命名空间 D
。
read
唯一可能的重载是 cv::read
,它需要 bool
.
将 read<T>
的声明移至上方 cv::operator >>
解决问题,也将考虑使用 ADL。
通常,我会将 类 和命名空间分隔到不同的 .hpp 文件中,这将自动强制您创建函数:
template<typename _Tp> static inline void operator >> (const FileNode& n, _Tp& value)
在使用它的命名空间之前声明(如cv
)。
我认为避免此类问题的最好方法是清理代码,并使其尽可能分离和独立。这样,无论何时你想在另一个命名空间中使用这个独特的 read
函数,你都不需要将新的命名空间放在这个文件中,或者包括当前在这个文件中的所有命名空间。