#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.
EDIT
Просто чтобы добавить несколько заключительных строк.В принципе не должно быть проблем с sfinae decltype.Хорошо, его не выводимый контекст, но почему это проблема?Единственное, о чем я могу подумать, это то, что в невыбранном контексте есть некоторые особые правила в сочетании с частичным упорядочением ...