Я только начинаю читать и изучать SFINAE. Чтобы улучшить мое понимание, я начал пробовать все сам.
Поэтому я задумался о полезном, но все же простом способе использования мощного трюка SFINAE, и я перестал думать о наборе функций, который вычисляет, сколько байтов занимает данный тип; до тех пор, пока мы имеем дело с простыми типами, решение тривиально:
template <typename T> size_t SizeOf(const T &t)
{
return sizeof(T);
};
Это наивное приближение получило бы размер чего угодно: 1 для char
возможно 4 для int
надеюсь 4 для char[4]
и что угодно для class PrettyAwesome
или же struct AmazingStuff
в том числе байты заполнения. Но как насчет динамической памяти, управляемой этими типами?
Поэтому я бы проверил, является ли данный тип типом указателя, тогда общий размер будет равен размеру указателя плюс размер остроконечной памяти (если есть).
template <typename T> size_t SizeOf(const T &*t)
{
size_t Result = sizeof(t);
if (t)
{
Result += sizeof(T);
}
return Result;
};
Да, на данный момент кажется, что SFINAE вообще не нужен, но давайте подумаем о контейнерах. SizeOf
контейнер должен быть суммой sizeof(container_type)
плюс сумма размера каждого из его элементов, вот куда входит SFINAE:
template <typename T> size_t SizeOf(const T &t)
{
size_t Result = sizeof(t);
for (T::const_iterator i = t.begin(); i != t.end(); ++i)
{
Result += SizeOf(*i);
}
return Result;
};
В приведенном выше коде определить, если ты T
тип имеет const_iterator
нужен, и если контейнер является картой, нужна специализация для пар.
Наконец, вопросы начинаются здесь: Что я пробовал и в каких проблемах застрял?
#include <type_traits>
#include <string>
#include <map>
#include <iostream>
#include <vector>
// Iterable class detector
template <typename T> class is_iterable
{
template <typename U> static char has_iterator(typename U::const_iterator *);
template <typename U> static long has_iterator(...);
public:
enum
{
value = (sizeof(has_iterator<T>(0)) == sizeof(char))
};
};
// Pair class detector
template <typename T> class is_pair
{
template <typename U> static char has_first(typename U::first_type *);
template <typename U> static long has_first(...);
template <typename U> static char has_second(typename U::second_type *);
template <typename U> static long has_second(...);
public:
enum
{
value = (sizeof(has_first<T>(0)) == sizeof(char)) && (sizeof(has_second<T>(0)) == sizeof(char))
};
};
// Pointer specialization.
template <typename T> typename std::enable_if<std::is_pointer<T>::value, size_t>::type SizeOf(const T &aValue)
{
size_t Result = sizeof(aValue);
if (aValue)
{
Result += sizeof(T);
}
return Result;
}
// Iterable class specialization.
template <typename T> typename std::enable_if<is_iterable<T>::value, size_t>::type SizeOf(const T &aValue)
{
size_t Result = sizeof(aValue);
for (T::const_iterator I = aValue.begin(); I != aValue.end(); ++I)
{
Result += SizeOf(*I);
}
return Result;
}
// Pair specialization.
template <typename T> typename std::enable_if<is_pair<T>::value, size_t>::type SizeOf(const T &aValue)
{
return SizeOf(aValue.first) + SizeOf(aValue.second);
}
// Array specialization.
template <typename T> typename std::enable_if<std::is_array<T>::value, size_t>::type SizeOf(const T &aValue)
{
size_t Result = sizeof(aValue);
for (T *I = std::begin(aValue); I != std::end(aValue); ++I)
{
SizeOf(*I);
}
return Result;
}
// Other types.
template <typename T> typename std::enable_if<std::is_pod<T>::value, size_t>::type SizeOf(const T &aValue)
{
return sizeof(aValue);
}
int main(int argc, char **argv)
{
int Int;
int *IntPtr = ∬
int twoints[2] = {0, 0};
int *twointpointers[2] = {IntPtr};
std::string SO("StackOverflow");
std::wstring WSO(L"StackOverflow");
std::map<std::string, char> m;
std::vector<float> vf;
m[SO] = 'a';
std::cout << "1: " << SizeOf(Int) << '\n';
// std::cout << "2: " << SizeOf(IntPtr) << '\n';
// std::cout << "3: " << SizeOf(twoints) << '\n';
// std::cout << "4: " << SizeOf(twointpointers) << '\n';
std::cout << "5: " << SizeOf(SO) << '\n';
std::cout << "6: " << SizeOf(WSO) << '\n';
std::cout << "7: " << SizeOf(m) << '\n';
std::cout << "8: " << SizeOf(vf) << '\n';
return 0;
}
Приведенный выше код производит этот вывод:
1: 4
5: 45
6: 58
7: 66
8: 20
Если я раскомментирую строки с выводом 2, 3 и 4, компилятор покажетнеоднозначный вызов«ошибка. Я действительно думал, что выход 2 будет использовать is_pointer
специализация и выход 3 и 4 будет использовать is_array
один. Ну, я был неправ, но я не знаю Зачем.
Я не в порядке с тем, как я получаю общий размер контейнера, я думаю, что итерация всех элементов и вызов SizeOf
для каждого товара хороший выбор, но не для всех контейнеров, в std::basic_string
дела sizeof(container) + sizeof(container::value_type) * container.size()
будет быстрее, но я не могу понять, как специализироваться на basic_string
.
Говоря о классы обнаружения (как те, которые обнаруживают итерируемый и пара), в некоторых блоги статьи и веб-примеры о SFINAE я видел, что это обычная практика для создания true_type
а также false_type
typedef
s, обычно определяется как char
а также char[2]
; но я обнаружил, что некоторые авторы используют char
а также long
как true_type
а также false_type
, Кто-нибудь знает, какая из них — лучшая практика или самый стандартный один?.
Обратите внимание, что я не ищу такие ответы, как почему вы не пытаетесь «эту библиотеку» или «этот инструмент», моя цель — практиковать и понимать СФИНА, любая подсказка и совет приветствуются.
Ваши «специализации» для указателей и т. Д. Фактически не являются специализациями. Они перегружены.
Компилятор сначала выполняет разрешение перегрузки, а затем проверяет специализации. Формально нет такой вещи как «неоднозначная специализация». Ваши случаи 2,3 и 4 уже терпят неудачу в разрешении перегрузки именно потому, что у вас нет специализаций.
Разрешение перегрузки определяется только для типов аргументов. Ваши перегрузки отличаются только типом возврата. Конечно, некоторые перегрузки могут быть отключены, но вам нужно будет отключить все перегрузки но один. В настоящее время массив POD допускает как перегрузки POD, так и массива.
Для контейнера лучшим решением, вероятно, является использование Container.size()
,
char[2]
предпочтительнее, потому что sizeof(long)
может быть 1, в соответствии со стандартом.
Один не заданный вопрос, на который я все равно отвечу: «Как мне тогда написать перегрузку массива»? Прикол есть ссылка на массив:
template<typename T, unsigned N>
constexpr size_t SizeOf(const T (&aValue)[N])
{
// return N * sizeof(T); If you want to do the work yourself
return sizeof(aValue); // But why bother?
}
1. Вы должны прочитать о концепции POD в C ++ 11. Массив элементов POD-типа или указатель на элемент POD-типа являются POD-типами http://en.cppreference.com/w/cpp/concept/PODType
Например, следующий код будет хорошо скомпилирован http://liveworkspace.org/code/81627f5acb546c1fb73a69c45f7cf8ec
2. Как-то так может помочь тебе
template<typename T>
struct is_string
{
enum
{
value = false
};
};
template<typename Char, typename Traits, typename Alloc>
struct is_string<std::basic_string<Char, Traits, Alloc>>
{
enum
{
value = true
};
};
функции
// Iterable class specialization.
template <typename T> typename std::enable_if<is_iterable<T>::value && !is_string<T>::value, size_t>::type SizeOf(const T &aValue)
{
size_t Result = sizeof(aValue);
for (typename T::const_iterator I = aValue.begin(); I != aValue.end(); ++I)
{
Result += SizeOf(*I);
}
return Result;
}
template <typename T> typename std::enable_if<is_string<T>::value, size_t>::type SizeOf(const T& aValue)
{
return sizeof(aValue) + sizeof(typename T::value_type) * aValue.length();
}
3. Нет информации в стандарте, что sizeof(long)
никогда не должно быть равным sizeof(char)
, но sizeof(char)
не может быть равным sizeof(char[2])
Итак, второй вариант предпочтительнее, я думаю.
Что касается вопроса № 3, я думаю, что в C ++ 11 гораздо чище (и более понятно) использовать decltype
вместо sizeof
чтобы получить интегральная константа такие как std::true_type
а также std::false_type
,
Например, ваш is_iterable
:
#include <type_traits> // std::true_type, std::false_type
// Iterable class detector
template <typename T> class is_iterable {
template <typename U> static std::true_type test(typename U::const_iterator *);
template <typename U> static std::false_type test(...);
public:
// Using decltype in separate typedef because of gcc 4.6 bug:
// http://gcc.gnu.org/bugzilla/show_bug.cgi?id=6709
typedef decltype(test<T>(0)) result_type;
static const bool value = result_type::value;
};