Область поиска символов символа С++ для шаблона и класса без шаблона? - программирование
Подтвердить что ты не робот

Область поиска символов символа С++ для шаблона и класса без шаблона?

#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-> не является параметром, поскольку я пытаюсь создать механизм "резервного копирования".

4b9b3361

Ответ 1

То, что вы получаете, является ожидаемым результатом. Это называется "двухфазным поиском имен" в стандарте С++.

Имена внутри шаблонов делятся на два типа:

Зависимые - имена, которые зависят от параметров шаблона, но arent, объявленных в шаблоне.

Независимые - имена, которые не зависят от параметров шаблона, плюс имя самого шаблона и объявленных в нем имен.

Когда компилятор пытается разрешить какое-либо имя в коде, он сначала решает, зависит ли имя или нет, и процесс разрешения проистекает из этого различия. Хотя не зависящие от имени имена разрешаются "нормально" - когда шаблон определен, разрешение для зависимых имен происходит в точке экземпляра шаблонов.

foo(); в B::call в вашем примере это имя, не зависящее от имени, поэтому оно разрешено глобальным foo() в точке определения шаблона.

Ответ 2

Принятый ответ объясняет, почему вы видите это поведение, но не как добиться требуемого "резервного" поведения. Это можно сделать, используя 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()
}

UPDATE: Я только что заметил ваши комментарии в другом ответе, где вы говорите, что знаете этот метод, но не можете его использовать из-за необходимости поддерживать дисфункциональные компиляторы. В этом случае я боюсь, что то, что вы хотите, возможно, невозможно.

Ответ 3

Вам нужно специально указать, чтобы использовать метод класса T.

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


Но что касается резервного механизма, вы можете проверить этот вопрос: Можно ли написать шаблон для проверки существования функции?

Использование Ошибка замены не является ошибкой (SFINAE), вы можете проверить метод foo в T, а затем запустить соответствующий метод.