Подтвердить что ты не робот

С++ Перегрузка шаблона с помощью enable_if: другое поведение с g++ и clang

Во время разрешения перегрузки шаблонизированной функции-члена базового класса я наблюдал другое поведение между g++ (5.2.1-23) и clang (3.8.0), с -std=c++14.

#include <iostream>
#include <type_traits>

struct Base
{
  template <typename T>
  auto a(T t) -> void {
    std::cout<< "False\n";
  }
};

template <bool Bool>
struct Derived : public Base
{

  using Base::a;
  template <typename T, bool B = Bool>
  auto a(T t) -> std::enable_if_t<B, void>
  {
    std::cout<< "True\n";
  }
};

int main()
{
  Derived<true> d;
  d.a(1); // fails with g++, prints "true" with clang
  Derived<false> d2;
  d2.a(1); // fails with clang++, prints "false" with g++
}

Вызов Derived<true>::a не выполняется с g++ со следующим сообщением:

test.cc: In function ‘int main()’:
test.cc:28:8: error: call of overloaded ‘a(int)’ is ambiguous
   d.a(1);
        ^
test.cc:18:8: note: candidate: std::enable_if_t<B, void> Derived<Bool>::a(T) [with T = int; bool B = true; bool Bool = true; std::enable_if_t<B, void> = void]
   auto a(T t) -> std::enable_if_t<B, void>
        ^
test.cc:7:8: note: candidate: void Base::a(T) [with T = int]
   auto a(T t) -> void {
        ^

и вызов Derived<false>::a завершается с помощью clang++ со следующим сообщением:

test.cc:32:6: error: no matching member function for call to 'a'
  d2.a(1);
  ~~~^
/usr/bin/../lib/gcc/x86_64-linux-gnu/5.2.1/../../../../include/c++/5.2.1/type_traits:2388:44: note: candidate template ignored: disabled by 'enable_if' [with T = int, B = false]
    using enable_if_t = typename enable_if<_Cond, _Tp>::type;
                                           ^

Я предполагаю, что они интерпретируют по-разному using Base::a; и что он не рассматривается в clang, тогда как он (может быть, слишком много) рассматривается в g++. Я думал, что если Derived имеет true в качестве параметра, тогда вызов a() отправляется в реализацию Derived, тогда как если параметр false, вызов отправляется на Base::a.

Они оба ошибаются? Кто прав? Кому я должен представить отчет об ошибке? Может кто-нибудь объяснить, что происходит?

Спасибо

4b9b3361

Ответ 1

От 3.3.10/p3 Скрытие имени [basic.scope.hiding]:

В определении функции-члена объявление имени в блоке scope скрывает объявление члена класса с тем же имя; см. 3.3.7. Объявление члена в производном классе (Раздел 10) скрывает объявление элемента базового класса то же имя; см. 10.2

Также 7.3.3/p15 Объявление using [namespace.udecl]:

Когда декларация using приносит имена из базового класса в производную класс, функции-члены и шаблоны функций-членов в производный класс переопределяет и/или скрывает функции-члены и член функциональные шаблоны с тем же именем, список параметров-параметров (8.3.5), cv-qualification и ref-qualifier (если есть) в базовом классе (скорее чем противоречивые). [Примечание. Для использования-объявлений, которые называют конструктор, см. 12.9. - end note] [Пример:

struct B {
  virtual void f(int);
  virtual void f(char);
  void g(int);
  void h(int);
};
struct D : B {
  using B::f;
  void f(int); // OK: D::f(int) overrides B::f(int);
  using B::g;
  void g(char); // OK
  using B::h;
  void h(int); // OK: D::h(int) hides B::h(int)
};
void k(D* p)
{
  p->f(1); // calls D::f(int)
  p->f(’a’); // calls B::f(char)
  p->g(1); // calls B::g(int)
  p->g(’a’); // calls D::g(char)
}

- конец примера]

Это разрешено во время поиска имени участника. Таким образом, он перед выводом аргумента шаблона. Следовательно, как правильно TC упоминается в комментариях Функция базового шаблона скрыта независимо от вердикта SFINAE.

Поэтому CLANG корректен и GCC ошибочен.