16
#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;
}

这给出了预期的结果

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;
}

我得到了意想不到的结果

global foo()

并且使用this->不是一种选择,因为我正在尝试创建“后备”机制。

4

3 回答 3

16

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

模板中的名称分为两种:

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

Non-dependent – 不依赖于模板参数的名称,加上模板本身的名称和在其中声明的名称。

当编译器试图解析代码中的某个名称时,它首先判断该名称是否依赖,而解析过程源于这种区别。虽然“正常”解析非依赖名称 - 当定义模板时,依赖名称的解析发生在模板实例化的点。

foo();B::call您的示例中,in 是一个非依赖名称,因此foo()在模板定义时它被解析为全局。

于 2012-04-20T14:54:47.133 回答
3

接受的答案解释了为什么你会看到这种行为,而不是如何实现你想要的“后备”行为。这可以使用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()
}

更新:我刚刚注意到您在另一个答案中的评论,您说您知道这种方法,但由于必须支持功能失调的编译器而无法使用它。在那种情况下,恐怕你想要的可能是不可能的。

于 2012-04-20T16:07:01.573 回答
0

您需要明确告诉使用 T 类方法。

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


但是对于回退机制,您可以检查这个问题:是否可以编写模板来检查函数的存在?

使用Substitution failure is not an error (SFINAE),您可以检查fooT 中的方法,然后运行正确的方法。

于 2012-04-20T14:54:09.890 回答