我正在解决一个经典问题:检查某个命名空间中是否存在自由函数。它被讨论,例如here。
然而,有一点点扭曲:函数的定义可能会出现以后而不是checker类。这是一个例子。
struct Yes {};
struct No {};
struct YesButLater {};
void f(Yes) {}
template<typename T, typename Enable = void>
struct HasF : public std::false_type {};
template<typename T>
struct HasF<T, decltype(void( ::f(T()) ))> : public std::true_type {};
void f(YesButLater) {}
int main() {
cout << HasF<Yes>::value << endl; // 1
cout << HasF<No>::value << endl; // 0
cout << HasF<YesButLater>::value << endl; // 0, expected 1
}
f(YesButLater)
的声明时间晚于HasF
辅助类,虽然我在定义f(YesButLater)
后实例化了模板,但帮助程序却没有注意到它。
所以,问题1:我该如何处理?
现在又一个更好奇的例子。
template<typename T>
struct HasF<T, decltype(void( f(T()) ))> : public std::true_type {};
void f(YesButLater) {}
void f(std::string) {}
int main() {
cout << HasF<YesButLater>::value << endl; // 1 (but what's the difference?)
cout << HasF<std::string>::value << endl; // 0, expected 1
}
请注意,我从::
表达式中删除了decltype(...)
。由于某种原因f(YesButLater)
被HasF
注意到,但f(std::string)
仍然模糊不清。
问题2:为什么我们在此示例中观察到::f(T())
和f(T())
的不同行为?此外,YesButLater
和std::string
之间有什么区别?
我认为命名空间查找有一些技巧,但我无法得到它。
答案 0 :(得分:3)
似乎我已经知道发生了什么。
当我写::f(...)
时,正在使用限定名称查找搜索名称f
。这种查找仅遇到在通话点之前可用的声明的名称。现在很明显为什么第一个版本找不到f(YesButLater)
:它的声明会在稍后发生。
当我写f(...)
时,会发生非限定名称查找。同样,它无法找到在调用point之前声明的任何名称。这里出现参数依赖查找。它会在f(T)
所属的整个命名空间中搜索T
。如果f(YesButLater)
此命名空间是全局的,则找到该函数。如果f(std::string)
ADL尝试搜索std::f(std::string)
,当然会失败。
以下是举例说明案例的两个例子。
namespace foo {
class C {};
}
template<typename T>
void call() {
f(T());
}
namespace foo {
void f(C) {}
}
int main() {
call<foo::C>();
}
此处使用ADL搜索f(T())
并找到call()
,但其声明在call()
之后。如果我们修改template<typename T>
void call() {
foo::f(T());
}
函数......
foo::f(T)
这导致编译错误,因为vector<Mat> images;
执行限定查找并且找不到所需的函数,因为此时没有可用的声明。