Мне интересно, реализована ли unordered_map
с использованием стирания типа, поскольку unordered_map
и unordered_map
могут использовать один и тот же код (за исключением приведения, которое в машинном коде не выполняется), то есть реализация обоих может быть основана на unordered_map
для экономии размера кода.
Обновление:Этот метод обычно называют идиомой тонкого шаблона(спасибо комментаторам ниже за указание на это).
Обновление 2:Меня особенно интересует мнение Говарда Хиннанта. Будем надеяться, что он это прочитает.
Итак, я написал этот небольшой тест:
#include
#if BOOST
# include
using boost::unordered_map;
#else
# include
using std::unordered_map;
#endif
struct A { A(int x) : x(x) {} int x; };
struct B { B(int x) : x(x) {} int x; };
int main()
{
#if SMALL
unordered_map ma, mb;
#else
unordered_map ma;
unordered_map mb;
#endif
ma["foo"] = new A(1);
mb["bar"] = new B(2);
std::cout << ((A*) ma["foo"])->x << std::endl;
std::cout << ((B*) mb["bar"])->x << std::endl;
// yes, it leaks.
}
И определил размер скомпилированного вывода с различными настройками:
#!/bin/sh
for BOOST in 0 1 ; do
for OPT in 2 3 s ; do
for SMALL in 0 1 ; do
clang++ -stdlib=libc++ -O${OPT} -DSMALL=${SMALL} -DBOOST=${BOOST} map_test.cpp -o map_test
strip map_test
SIZE=$(echo "scale=1;$(stat -f "%z" map_test)/1024" | bc)
echo boost=$BOOST opt=$OPT small=$SMALL size=${SIZE}K
done
done
done
Получается, что со всеми настройками, которые я пробовал, много внутреннего кода unordered_map
создается дважды:
With Clang and libc++:
| -O2 | -O3 | -Os
-DSMALL=0 | 24.7K | 23.5K | 28.2K
-DSMALL=1 | 17.9K | 17.2K | 19.8K
With Clang and Boost:
| -O2 | -O3 | -Os
-DSMALL=0 | 23.9K | 23.9K | 32.5K
-DSMALL=1 | 17.4K | 17.4K | 22.3K
With GCC and Boost:
| -O2 | -O3 | -Os
-DSMALL=0 | 21.8K | 21.8K | 35.5K
-DSMALL=1 | 16.4K | 16.4K | 26.2K
(с компиляторами из Apple Xcode)
Теперь вопрос: есть ли какая-то убедительная техническая причина, по которой разработчики решили пропустить эту простую оптимизацию?
Также: какого черта эффект -Os
прямо противоположен тому, что рекламируется?
Обновление 3:
Как предложил Никол Болас, я повторил измерения с shared_ptr
вместо голых указателей (созданных с помощью make_shared
и приведение с помощью static_pointer_cast
). Тенденция результатов та же:
With Clang and libc++:
| -O2 | -O3 | -Os
-DSMALL=0 | 27.9K | 26.7K | 30.9K
-DSMALL=1 | 25.0K | 20.3K | 26.8K
With Clang and Boost:
| -O2 | -O3 | -Os
-DSMALL=0 | 35.3K | 34.3K | 43.1K
-DSMALL=1 | 27.8K | 26.8K | 32.6K