Правильная подпись/обнаружение присутствия Container:: reserve()

Учитывая тип C, который является контейнером, совместимым с STL, как правильно определить, содержит ли C функцию-член reserve? Я попробовал следующий подход (с GCC 4.6.3):

template< typename C, typename = void >
struct has_reserve
  : std::false_type
{};

template< typename C >
struct has_reserve< C, typename std::enable_if<
                         std::is_same<
                           decltype( &C::reserve ),
                           void (C::*)( typename C::size_type )
                         >::value
                       >::type >
  : std::true_type
{};

Это работает для C std::vector, но не для неупорядоченных контейнеров, например. std::unordered_set. Причина в том, что reserve является (прямой) функцией-членом std::vector, но для неупорядоченных контейнеров она наследуется от базового класса, то есть ее подпись не void (C::*)( typename C::size_type ), а void (B::*)( typename C::size_type ) для некоторой неопределенной базы class B of C.

Я знаю, как обойти его и обнаружить reserve, даже если он унаследован, но он выглядит неуклюжим, и мне интересно, что разрешено стандартом. Так что...

Мой вопрос: позволяет ли стандарт reserve унаследоваться от неуказанного базового класса или является привязкой к синопсису и требует прямой функции-члена?

Ответ 1

Весь стандарт говорит о наследовании от базовых классов, что разрешено:

17.6.5.11 Производные классы [вывод]

1 - реализация может выводить любой класс в стандартной библиотеке С++ из класса с именем, зарезервированным для реализации.

В обоих случаях не указано, разрешены ли методы (и, действительно, другие члены, такие как typedefs), наследуемые от базового класса; очевидно, так как реализации делают это, стандарт должен описывать это поведение.

В любом случае, обнаружение, например, reserve путем приведения в тип функции-члена не гарантируется работа, даже если член самого производного типа, поскольку:

17.6.5.5 Функции-члены [member.functions]

2 - реализация может объявить дополнительные не виртуальные сигнатуры функций-членов внутри класса:

  • путем добавления аргументов со значениями по умолчанию в подпись функции-члена 186; [...]

186) Следовательно, адрес функции-члена класса в стандартной библиотеке С++ имеет неопределенный тип.

Правильный способ проверить, существует ли reserve, попытаться вызвать его:

template< typename C, typename = void >
struct has_reserve
  : std::false_type
{};

template< typename C >
struct has_reserve< C, typename std::enable_if<
                         std::is_same<
                           decltype( std::declval<C>().reserve( std::declval<typename C::size_type>() ) ),
                           void
                         >::value
                       >::type >
  : std::true_type
{};

Это имеет преимущество параллельного требования к контейнеру (таблица 103 для unordered_set), которые являются нормативными, когда синонимы больше ориентируются на информативность.