Разрешение перегрузки шаблонов функций
Имеется следующий код (максимально упрощен в данный момент, но в конце приведён полностью):
template<
typename _Type,
size_t _Idx,
size_t... _Idxs
>
constexpr auto _CountFieldsImp( size_t& output, std::index_sequence<_Idx, _Idxs...> fields_indices )
-> decltype( /* ... */ )
{ /* ... */ }
template<
typename _Type,
size_t... _Idxs
>
constexpr void _CountFieldsImp( size_t& output, std::index_sequence<_Idxs...> /* unused */ )
{
_CountFieldsImp<_Type>(
output, std::make_index_sequence<sizeof...( _Idxs ) - 1>{}
);
}
Проблема состоит в следующем: используя компилятор MSVC (версия 19.16 для х64) получаю ошибку компиляции, которая гласит о том, что невозможно выбрать из двух возможных перегрузок (успешно инстанцируется первая функция, что порождает вторую возможную альтернативу при перегрузке наряду со второй функцией). Место возникновения ошибки отмечено в полной версии код ниже (см. CountFields).
В то же время, код успешно компилируется с помощью различных версий gcc и clang. Что интересно, на сайте GodBolt аналогичный код спокойно компилируется и с помощью MSVC 19.16 (пример тут), а также более ранних версий.
В чем может быть корень проблемы? Насколько я понимаю, первая функция считается более специализированной, а значит должны выигрывать при разрешении перегрузки.
Стандарт языка: С++14.
Версия Visual Studio: Visual Studio Enterprise 2017 15.9.19
Полный код приведён ниже:
#include <iostream>
#include <string>
#include <type_traits>
#include <utility>
class CTestClass
{
public:
std::string field1;
int field2;
double field3;
};
template<size_t /* _Idx */>
struct _ConvertibleToAny
{
template<typename _Type>
constexpr operator _Type()
const noexcept( std::is_nothrow_constructible<_Type>::value )
{
return _Type{};
};
};
template<typename _Type>
struct _MaxFieldsCount
{
static constexpr size_t Count = 8 * sizeof( _Type );
};
template<
typename _Type, /* POD структура */
size_t _Idx, /* индексы */
size_t... _Idxs
>
constexpr auto _CountFieldsImp( size_t& output, std::index_sequence<_Idx, _Idxs...> fields_indices )
noexcept( std::is_nothrow_constructible<_Type>::value )
-> decltype(
/* попробуем построить наш тип данных с заданным числом аргументов */
_Type{ _ConvertibleToAny<_Idx>{}, _ConvertibleToAny<_Idxs>{}... }
)
{
output = fields_indices.size();
return _Type{};
}
template<
typename _Type,
size_t... _Idxs
>
constexpr void _CountFieldsImp( size_t& output, std::index_sequence<_Idxs...> /* unused */ )
{
_CountFieldsImp<_Type>(
output, std::make_index_sequence<sizeof...( _Idxs ) - 1>{}
);
}
template<typename _Type>
constexpr size_t CountFields( const _Type& )
{
size_t result = 0;
//
// Вот тут ругается
//
_CountFieldsImp<_Type>(
result, std::make_index_sequence<_MaxFieldsCount<_Type>::Count>()
);
return result;
}
int main()
{
CTestClass test;
std::cout << CountFields( test );
return 0;
}