2017-03-16 17 views
3

我正在解决一个经典问题:检查某些名称空间中是否存在自由函数。讨论了例如here模板元编程:检查后面定义的函数的存在

但是,有一个轻微的扭曲:函数的定义可能会出现后面比检查器类。这是一个例子。

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())的不同行为?此外,YesButLaterstd::string之间的区别是什么?

我认为有一些与命名空间查找技巧,但我无法得到的东西。

+0

我的猜测 - 这是由于[adl](http://en.cppreference.com/w/cpp/language/adl)规则... –

+0

@ W.F。是的,可能,尽管我仍然不明白为什么::重要,因为我在示例中始终使用全局名称空间。 –

回答

3

看来我已经知道发生了什么。

当我编写::f(...)时,名称f正在搜索限定名称查找。这种查找只会遇到直到调用点的声明。现在很清楚为什么第一个版本找不到f(YesButLater):它的声明稍后发生。

当我写f(...),不合格名称查找发生。同样,它没有找到任何在呼叫点之前声明的名称。这里参数依赖查找出现。它在整个名称空间中搜索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>(); 
} 

这里f(T())正在搜索与ADL和发现,虽然其声明是call()后。如果我们修改call()功能...

template<typename T> 
void call() { 
    foo::f(T()); 
} 

这会导致编译错误,因为foo::f(T)执行合格查找和不申报可在一时无法找到所需的功能。