Я задавался вопросом, реализуется ли unordered_map
с использованием стирания типа, поскольку unordered_map<Key, A*>
и unordered_map<Key, B*>
могут использовать точно такой же код (кроме кастинга, который не является оператором в машинный код). То есть реализация обоих может быть основана на unordered_map<Key, void*>
для сохранения размера кода.
Обновление: Этот метод обычно называют Thin Template Idiom (спасибо комментаторам ниже для указав это).
Обновление 2: Я был бы особенно заинтересован в Говарде Хиннанте. Пусть он надеется, что он это прочитает.
Итак, я написал этот небольшой тест:
#include <iostream>
#if BOOST
# include <boost/unordered_map.hpp>
using boost::unordered_map;
#else
# include <unordered_map>
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<std::string, void*> ma, mb;
#else
unordered_map<std::string, A*> ma;
unordered_map<std::string, B*> 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<void/A/B>
вместо голых указателей (созданных с помощью 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