Различия в поиске имен между g++ и MSVS

Рассмотрим этот код:

#include <iostream>

namespace N {
    class A {};
    void f(A a) { std::cout << "N::f\n"; }
}

void f(int i) { std::cout << "::f\n"; }

template <typename T>
class Base {
  public:
    void f(T x) { std::cout << "Base::f\n"; }
};


template <typename T>
class X : public Base<T> {
  public:
    void g() {
        T t;
        f(t);
    }
};

int main()
{
    X<N::A> x1;
    x1.g();

    X<int> x2;
    x2.g();
}

Код предназначен для изучения того, как работает поиск имен в C++.

Если я скомпилирую эту программу с помощью GNU C++ (версия 6.1.0), она напечатает:

N::f
::f

Но если я скомпилирую его с помощью Microsoft Visual Studio 2015, он напечатает:

Base::f
Base::f

Какое поведение является правильным и почему?


person oz1cz    schedule 30.08.2016    source источник
comment
Я не знаю ответа на этот вопрос, скажем так, но я знаю, что компилятор Microsoft Visual Studio C++ часто не соответствует стандарту. (Поэтому я предполагаю, что Microsoft VS неверен)   -  person DarthRubik    schedule 30.08.2016
comment
Тем не менее, VS 2015 должен быть очень близок к стандарту. После быстрой проверки Clang ведет себя как GCC. Поэтому, не зная подробностей, я бы также предположил, что VS ошибается. Возможно, вы наткнулись на ошибку здесь.   -  person Hayt    schedule 30.08.2016


Ответы (2)


g++ здесь совместим со стандартом, а Visual C++ нет:

14.6.2 Зависимые имена [temp.dep]

3 В определении класса или шаблона класса область действия зависимого базового класса (14.6.2.1) не проверяется при поиске неполного имени ни в точке определения шаблона класса, ни в члене, ни во время создания экземпляра шаблона класса или члена.

Замена f() на this->f() найдет базовый член.

person TemplateRex    schedule 30.08.2016
comment
Спасибо, TemplateRex. Интересно, почему в С++ есть это правило. Это потому, что специализация шаблона может привести к классу Base без функции f? - person oz1cz; 30.08.2016
comment
@ oz1cz точно! Об этом есть целый отрывок (9.4.2.) в справочнике C++ Templates the Complete Guide. - person TemplateRex; 30.08.2016

В определении функции g имя f рассматривается как функция, объявленная вне класса (в определении класса это имя не объявляется; f — зависимое имя).

template <typename T>
class X : public Base<T> {
  public:
    void g() {
        T t;
        f(t);
    }
};

Поэтому компилятор использует поиск ADL.

Однако если написать явный вызов функции-члена

class X : public Base<T> {
  public:
    void g() {
        T t;
        this->f(t);
    }
};

то вызов функции f будет считаться вызовом функции-члена базового класса..

Таким образом, кажется, что в MS VC++ 2015 есть ошибка.

person Vlad from Moscow    schedule 30.08.2016
comment
Почему f считается именем, объявленным вне области действия класса? Является ли это нормальным поведением, и оно возвращается в область класса только в том случае, если ничего не может быть найдено вне класса? - person NathanOliver; 30.08.2016
comment
@NathanOliver Это особое поведение классов, производных от шаблона. У меня нет под рукой стандарта С++, поэтому я не могу получить соответствующую цитату. - person Vlad from Moscow; 30.08.2016
comment
Это интересно. Мне придется поискать. g++, похоже, даже не отступает, так как этот не компилируется. - person NathanOliver; 30.08.2016
comment
@NathanOliver Это подтверждает то, что я написал. Имя f, выглядящее вне определения класса (поскольку в определении класса шаблона нет члена с именем f), не существует. - person Vlad from Moscow; 30.08.2016
comment
@NathanOliver См. этот вопрос stackoverflow.com/questions/27178483/ - person Vlad from Moscow; 30.08.2016