Частная специализация шаблона для интегральных непиговых параметров и нецелых не-типов, разница между g++ и clang
Ниже приведена простая частичная специализация шаблона:
// #1
template <typename T, T n1, T n2>
struct foo {
static const char* scenario() {
return "#1 the base template";
}
};
// #2
// partial specialization where T is unknown and n1 == n2
template <typename T, T a>
struct foo<T, a, a> {
static const char* scenario() {
return "#2 partial specialization";
}
};
Ниже приведены различные результаты по g++ (6.1) и clang++ (3.8.0):
extern const char HELLO[] = "hello";
double d = 2.3;
int main() {
cout << foo<int, 1, 2> ::scenario() << endl;
cout << foo<int, 2, 2> ::scenario() << endl;
cout << foo<long, 3, 3> ::scenario() << endl;
cout << foo<double&, d, d> ::scenario() << endl;
cout << foo<double*, &d, &d> ::scenario() << endl;
cout << foo<double*, nullptr, nullptr> ::scenario() << endl;
cout << foo<int*, nullptr, nullptr> ::scenario() << endl;
cout << foo<nullptr_t, nullptr, nullptr> ::scenario() << endl;
cout << foo<const char*, HELLO, HELLO> ::scenario() << endl;
}
результаты по g++ и clang
# | The code | g++ (6.1) | clang++ (3.8.0) |
1 | foo<int, 1, 2> | #1 as expected | #1 as expected |
2 | foo<int, 2, 2> | #2 as expected | #2 as expected |
3 | foo<long, 3, 3> | #2 as expected | #2 as expected |
4 | foo<double&, d, d> | #1 -- why? | #2 as expected |
5 | foo<double*, &d, &d> | #2 as expected | #2 as expected |
6 | foo<double*, nullptr, nullptr> | #2 as expected | #1 -- why? |
7 | foo<int*, nullptr, nullptr> | #2 as expected | #1 -- why? |
8 | foo<nullptr_t, nullptr, nullptr> | #2 as expected | #1 -- why? |
9 | foo<const char*, HELLO, HELLO> | #2 as expected | #2 as expected |
Какой из них правильный?
Код: http://coliru.stacked-crooked.com/a/45ba16c9f021fd84
Ответы
Ответ 1
Я думаю, что clang nullptr
больше похож на встроенную переменную типа типа in-build int
. nullptr
фактически не имеет типа. Объявление (определение отсутствует) nullptr_t
равно struct nullptr_t nullptr_t;
. И ответ на вопрос один, который прав... ответ заключается в том, что оба они правы, потому что есть стандарт, но это разные компиляторы, созданные из разных компаний, поэтому могут быть различия между GNU G++
и Clang
. Clang
должен быть полностью совместим с G++
, но это не так.
Ответ 2
# 4 плохо сформирован, я удивлен, что он компилируется. Во-первых, двойной параметр нельзя использовать как параметр шаблона непигового типа. С другой стороны, шаблон класса использует те же правила, что и шаблоны функций для частичного упорядочения. В стандарте приведен пример "мнимых" функций, сгенерированных для выполнения заказа:
template<int I, int J, class T> class X { };
template<int I, int J> class X<I, J, int> { }; // #1
template<int I> class X<I, I, int> { }; // #2
template<int I, int J> void f(X<I, J, int>); // A
template<int I> void f(X<I, I, int>); // B
В вашем примере это выглядит примерно так:
template <typename T, T n1, T n2>
struct foo {
};
template <typename T, T n1, T n2>
void bar(foo<T, n1, n2>)
{
std::cout << "a";
}
template <typename T, T a>
void bar(foo<T, a, a>)
{
std::cout << "b";
}
Вывод аргумента шаблона используется для определения того, какая функция более специализирована, чем другая. A double&
следует выводить как double
, и поэтому обе специализации должны быть равны, а также для двузначных для bar(foo<double&, d, d>{});
. Вот и GCC и Clang жалуются:
Ошибка GCC
main.cpp: 14: 6: примечание: вывод аргумента шаблона/замена failed: main.cpp: 26: 29: note: несогласованные типы 'double &' а также 'Двойной'
bar(foo<double&, d, d>{});
^
Ошибка Clang
Примечание: шаблон кандидата игнорируется: сбой замены [с T = double &]: выведенный аргумент шаблона не-типа не имеет того же введите в качестве соответствующего параметра шаблона ('double' vs 'double & Амп; ')
И снова, если вы удалите ссылку, они оба правильно жалуются на использование double
в качестве параметра непигового шаблона.
Я не буду тестировать остальные, но вы можете найти похожие результаты.