C ++符号范围搜索顺序与模板和非模板类不同?

时间:2012-04-20 14:42:41

标签: c++ templates scope

#include <iostream>

void foo()
{
    std::cout << "global foo()" << std::endl;
}

struct A {
    void foo()
    {
        std::cout << "A::foo()" << std::endl;
    }
};

struct B : public A {
    void call()
    {
        foo();
    }
};

int main(int argc, char **argv )
{
    B b;
    b.call();
    return 0;
}

这会给expected result

A::foo()

然而,在更改两行(B类到模板)之后:

#include <iostream>

void foo()
{
    std::cout << "global foo()" << std::endl;
}

struct A {
    void foo()
    {
        std::cout << "A::foo()" << std::endl;
    }
};

template <typename T> // change here
struct B : public T {
    void call()
    {
        foo();
    }
};

int main(int argc, char **argv )
{
    B<A> b; // and here
    b.call();
    return 0;
}

我得到unexpected result

global foo()

使用this->不是一个选项,因为我正在尝试创建一个&#34;后备&#34;机构。

3 个答案:

答案 0 :(得分:16)

您得到的是预期结果。这在C ++标准中称为“两阶段名称查找”。

模板内的名称分为两种类型:

依赖 - 依赖于模板参数但未在模板中声明的名称。

非依赖 - 不依赖于模板参数的名称,以及模板本身的名称和在其中声明的名称。

当编译器尝试解析代码中的某个名称时,它首先会确定该名称是否依赖,并且解析过程源于此区别。虽然非依赖名称是“正常”解析的 - 当定义模板时,依赖名称的解析发生在模板实例化的时刻。

示例中foo();中的

B::call是非依赖名称,因此在模板定义时将其解析为全局foo()

答案 1 :(得分:3)

接受的答案解释了为什么你会看到这种行为,而不是如何实现&#34;后备&#34;你想要的行为。这可以通过引入一对成员模板重载来使用SFINAE来完成,其中一个重载仅在基类具有名为foo的成员函数时才存在。

template <typename T>
struct B : T {
    template <void (T::*)()> struct has_mem_fn {};

    template <typename U> void call(has_mem_fn<&U::foo>*) {this->foo();}
    template <typename U> void call(...) {foo();}

    void call() {call<T>(0);}
};

struct X {};

int main()
{
    B<A> ba;
    ba.call();  // A::foo()

    B<X> bx;
    bx.call();  // global foo()
}

更新:我刚刚在另一个答案中注意到您的评论,您说您已经了解此方法,但由于必须支持功能失调的编译器,因此无法使用它。在那种情况下,我担心你想要的东西可能是不可能的。

答案 2 :(得分:0)

你需要具体告诉使用T类方法。

template <typename T>
struct B : public T {
    void call()
    {
        T::foo();
    }
};


但是对于后备机制,您可以查看以下问题:Is it possible to write a template to check for a function's existence?

使用替换失败不是错误(SFINAE),您可以在T中检查方法foo,然后运行正确的方法。