Ответ 1
#include <iostream>
#include <array>
#include <vector>
template <typename T, typename SFINAE=void>
struct trait;
template <typename T>
struct trait<T, std::void_t<decltype(std::declval<T>().begin()),
decltype(std::declval<T>().end())>> {
static const char* name() { return "Container"; }
};
template <typename T, std::size_t N>
struct trait<std::array<T,N>,void> {
static const char* name() { return "std::array"; }
};
int main(int argc, char* argv[]) {
std::cout << trait<std::vector<int>>::name() << std::endl;
std::cout << trait<std::array<int,2>>::name() << std::endl;
}
РЕДАКТИРОВАТЬ
Во-первых, нет гарантии, что следующее будет более угадать, чем доказать. Возможно, кто-то другой может исправить, расширить копию, вставьте его или что-то еще.
Однако моя первая догадка, увидев вопрос, заключалась в использовании std::void_t
. Я почти уверен, что раньше видел что-то подобное, но да, тоже не гарантировал бы этого. Чтобы показать, что можно использовать std::void_t
, мы должны показать, что "одна специализация шаблона более конкретна, чем другая". И мы делаем это, проверяя частичный порядок. Я буду подражать приведенному выше со следующим, что немного короче.
template <typename T, typename SFINAE=void>
struct trait;
//#1
template <typename T>struct trait<T, std::void_t<decltype(std::declval<T>().begin())>>
{
static const char* name() { return "Container"; }
};
//#2
template <typename T>struct trait<std::vector<T>,void> {
static const char* name() { return "std::vector"; }
};
Я не собираюсь объяснять, как делается частичный заказ, займет слишком много времени. После преобразования в функции и т.д. Вы получите нечто похожее на следующее.
//#2 from #1: f(trait<std::vector<T>,void>) from f(trait<__ANY_TYPE__, std::void_t<decltype(std::declval<__ANY_TYPE__>().begin())>)
//P=trait<std::vector<T>,void>
//A=trait<__ANY_TYPE__, std::void_t<decltype(std::declval<__ANY_TYPE__>().begin())>>
//P1=std::vector<T>
//A1=__ANY_TYPE__
//P2=void
//A2=std::void_t<decltype(std::declval<__ANY_TYPE__>().begin())>
//==> T=? --> fail, #2 from #1 is not working
Теперь мы должны показать, что # 1 из # 2 работает. Если это так, мы показали, что №2 более специализирован.
//#1 from #2: f(trait<T, std::void_t<decltype(std::declval<T>().begin())>>) from f(trait<std::vector<__ANY_TYPE__>,void>)
//P=trait<T, std::void_t<decltype(std::declval<T>().begin())>>
//A=trait<std::vector<__ANY_TYPE__>,void>
//P1=T
//A1=std::vector<__ANY_TYPE__>
//P2=std::void_t<decltype(std::declval<T>().begin())> //(*)
//A2=void
//==> T=std::vector<__ANY_TYPE__> ok #1 from #2 works
Это в основном мой эскиз, не проверяя стандарт или что-то еще. Я уверен, вы можете найти его где-то в бесконечных линиях стандарта...
Если вы обратили внимание, вы заметите (*). Эта строка в основном является единственной важной, если вы хотите использовать decltype (...). Я предполагаю, что использование decltype (...) приводит к не выводимому контексту для правой стороны, что, возможно, не позволяет использовать T из вычитания P1/A1. Но да, это в основном причина, почему я не включил ответ сначала в рабочее решение std::void_t
. Наконец, альтернативное определение std::void_t
с typename... Я думаю, что не выводимый контекст тоже так же, как decltype (...), из-за части typename.
РЕДАКТИРОВАТЬ
Просто добавьте несколько окончательных строк. В принципе не должно быть проблем с decltype sfinae. Хорошо, что он не выводил контекст, но почему это проблема? Единственное, о чем я могу думать, это то, что в невыводимом контексте есть специальные правила в сочетании с частичным упорядочением...