Частичное упорядочение в сочетании с параметром шаблона шаблона и шаблонами с переменными параметрами
У меня есть вопрос о специализации шаблонов в сочетании с переменными шаблонами и параметрами / аргументами шаблонных шаблонов.
Следующая маленькая программа компилируется с Clang 6.0.1
а также GCC 8.1.1
(Цель: x86_64-pc-linux-gnu
).
#include <iostream>
struct IndexSlot3 {
static constexpr std::size_t size = 3;
};
template<typename T_, typename... IndexSlots_>
struct Tensor {
Tensor() { std::cout << "Order N picked" << std::endl; }
};
template<typename T_, typename IndexSlot0_>
struct Tensor<T_, IndexSlot0_> {
Tensor() { std::cout << "Order 1 picked" << std::endl; }
};
template<typename T_, typename IndexSlot0_, typename IndexSlot1_>
struct Tensor<T_, IndexSlot0_, IndexSlot1_> {
Tensor() { std::cout << "Order 2 picked" << std::endl; }
};
int main(int argc, char** argv) {
Tensor<int, IndexSlot3> t1;
Tensor<int, IndexSlot3, IndexSlot3> t2;
Tensor<int, IndexSlot3, IndexSlot3, IndexSlot3> t3;
return 0;
}
Скомпилировать команды:
clang++ -std=c++17 main.cpp -o main -Wall -Wpedantic
g++ -std=c++17 main.cpp -o main -Wall -Wpedantic
Запуск программы печатает
Order 1 picked
Order 2 picked
Order N picked
с обоими компиляторами, как и ожидалось.
Тем не менее, когда я распространяю вышеизложенное на следующую программу, где Tensor
также принимает параметр шаблона шаблона, определяющий хранилище, Clang выбирает основной шаблон во всех трех случаях, в то время как GCC по-прежнему выбирает специализации.
#include <iostream>
struct IndexSlot3 {
static constexpr std::size_t size = 3;
};
template<typename T_, std::size_t... sizes>
struct CArrayStorage {
using Type = T_*;
};
template<typename T_, std::size_t size0_>
struct CArrayStorage<T_, size0_> {
using Type = T_[size0_];
};
template<typename T_, std::size_t size0_, std::size_t size1_>
struct CArrayStorage<T_, size0_, size1_> {
using Type = T_[size0_][size1_];
};
template<typename T_,
template<typename, std::size_t...> typename Storage_,
typename... IndexSlots_>
struct Tensor {
typename Storage_<T_, IndexSlots_::size...>::Type _storage;
Tensor() { std::cout << "Order N picked" << std::endl; }
};
template<typename T_,
template<typename, std::size_t> typename Storage_,
typename IndexSlot0_>
struct Tensor<T_, Storage_, IndexSlot0_> {
typename Storage_<T_, IndexSlot0_::size>::Type _storage;
Tensor() { std::cout << "Order 1 picked" << std::endl; }
};
template<typename T_,
template<typename, std::size_t, std::size_t> typename Storage_,
typename IndexSlot0_, typename IndexSlot1_>
struct Tensor<T_, Storage_, IndexSlot0_, IndexSlot1_> {
typename Storage_<T_, IndexSlot0_::size, IndexSlot1_::size>::Type _storage;
Tensor() { std::cout << "Order 2 picked" << std::endl; }
};
int main(int argc, char** argv) {
Tensor<int, CArrayStorage, IndexSlot3> t1;
Tensor<int, CArrayStorage, IndexSlot3, IndexSlot3> t2;
Tensor<int, CArrayStorage, IndexSlot3, IndexSlot3, IndexSlot3> t3;
return 0;
}
Вывод с Clang:
Order N picked
Order N picked
Order N picked
Выход с GCC:
Order 1 picked
Order 2 picked
Order N picked
Я думаю, что GCC прав, потому что специализация и основной матч одинаково хорошо, поэтому специализация должна быть выбрана. Но я не совсем уверен, как введение параметра шаблона шаблона меняет положение вещей.
Должен ли я сообщить об ошибке в Clang? Против GCC? Против обоих? Я забрел в область неопределенного поведения, не осознавая этого?
Любая помощь будет принята с благодарностью.
[Примеры не совсем минимальны, но я хотел предоставить немного контекста, чтобы вы могли понять, что я пытаюсь сделать.]