Шаблоны с одинаковой подписью не вызывают ошибку компиляции

Следующая программа определяет два шаблона функций, A::foo<>() а также B::foo<>()в двух отдельных пространствах имен (A а также B). Два функциональных шаблона идентичны в подписи, и отличаются только аргументом по умолчанию, назначенным их второму параметру шаблона. В конце концов, их имена попадают в сферу main() соответствующей парой using декларации:

#include <type_traits>

namespace A
{
template<
typename T,
typename = typename std::enable_if<
std::is_same<T, int>::value        // Let this be condition C
>::type
>
void foo(T) { }
}

namespace B
{
template<
typename T,
typename = typename std::enable_if<
!std::is_same<T, int>::value       // This is the negation of C
>::type
>
void foo(T) { }
}

int main() {
using A::foo;
using B::foo; // COMPILES: Is this legal?

foo(42); // Invokes A::foo(), non-ambiguous because of SFINAE
}

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

К моему удивлению, каждый компилятор, на котором я это пробовал (GCC 4.7.2, GCC 4.8.0 beta, ICC 13.0.1, Clang 3.2) компилирует программу и вызывает A::foo(),

ВОПРОС № 1: это правильно? Это возможно случай «Диагностика не требуется«Ссылки на стандарт C ++ 11 приветствуются.


Рассмотрим теперь этот вариант вышеуказанной программы, который в основном достигает того же эффекта, используя классы, а не пространства имен:

#include <type_traits>

struct X
{
template<
typename T,
typename = typename std::enable_if<
std::is_same<T, int>::value        // Here is condition C again
>::type
>
static void foo(T) { }
};

struct Y
{
template<
typename T,
typename = typename std::enable_if<
!std::is_same<T, int>::value       // And the negation of C again
>::type
>
static void foo(T) { }
};

struct Z : X, Y
{
using X::foo;
using Y::foo; // COMPILES: Is this legal?
};

int main() {
Z::foo(42); // Invokes X::foo(), non-ambiguous because of SFINAE
}

Эта программа также компилируется на всех вышеупомянутых компиляторах, в то время как я ожидаю, что ошибка компилятора будет вызвана второй using декларация.

ВОПРОС № 2: это правильно? Это возможно случай «Диагностика не требуется«Ссылки на стандарт C ++ 11 приветствуются.

2

Решение

По вопросу № 1: кажется, что это разрешено, потому что нет правила, запрещающего это. Стандарт C ++ 11 упоминает об этом в примечании рядом с правилом, запрещающим это, если функция, введенная объявлением using, конфликтует с функцией, которая объявлена ​​непосредственно в пространстве имен, в которое объявление using вводит имя.

Это говорит в §7.3.3 [namespace.udecl] / 14:

Если объявление функции в области пространства имен или блока имеет
то же имя и те же типы параметров, что и функция, представленная
использование-объявление, и объявления не объявляют то же самое
функция, программа плохо сформирована. […]

Это нормативный текст, который определяет определенный тип конфликта как недействительный.

Нет нормативного текста о том, что два использующих объявления не конфликтуют одинаково, но обратите внимание, что аналогичный конфликт между двумя использующими объявлениями недопустим в точке объявления. Тот же абзац продолжается:

[…] [Примечание: два объявления-использования могут вводить функции с
то же имя и те же типы параметров. Если для вызова неквалифицированного
имя функции, разрешение перегрузки функции выбирает функции
введенный такими объявлениями об использовании, вызов функции
плохо сформирован.
[ Пример:

 namespace B {
void f(int);
void f(double);
}
namespace C {
void f(int);
void f(double);
void f(char);
}

void h() {
using B::f;    // B::f(int) and B::f(double)
using C::f;    // C::f(int), C::f(double), and C::f(char)
f(’h’);        // calls C::f(char)
f(1);          // error: ambiguous: B::f(int) or C::f(int)?
void f(int);   // f(int) conflicts with C::f(int) and B::f(int)
}

— конец примера] — конец заметки]

Для вопроса № 2 аналогичный нормативный текст, который описывает случай конфликта между объявлением члена класса и объявлением использования уровня класса, находится в следующем абзаце, §7.3.3 / 15:

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

Опять же, нет текста о конфликтах между использованием объявлений, но аналогично примечанию в предыдущем случае, наличие двух объявлений использования, которые потенциально обозначают конфликтующие функции, не является неправильным, потому что нет текста, который запрещает сосуществование этих объявлений. И как в предыдущем случае: если для вызова функции разрешение перегрузки выбирает обе эти функции, то вызов неверен.

В вашем примере SFINAE всегда удаляет одну из потенциально конфликтующих функций из набора перегрузки, поэтому ни в одном из случаев проблем не возникает.

4

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]