Разрешение перегрузки шаблонов функций

Имеется следующий код (максимально упрощен в данный момент, но в конце приведён полностью):

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;
}

Ответы (0 шт):