Как частично специализировать фабричную структуру для всех `std :: array`s с более чем` 4` элементами? - PullRequest
0 голосов
/ 21 февраля 2019

Я хочу отправить вызов из фабричного шаблона функции в структуру, которая частично специализирована, на основе типа возврата:

#include <array>

template<typename Type, typename Enable=void> 
struct make_it; 

template<typename A> 
struct make_it<std::array<A, 3>>
{
    static std::array<A, 3> apply()
    {
        return {A{0}, A{1}, A{2}}; 
    }
};

template<typename A> 
struct make_it<std::array<A, 4>>
{
    static std::array<A, 4> apply()
    {
        return {A{0}, A{0}, A{0}, A{0}}; 
    }
};

template<typename T> 
constexpr bool greater(T&& a, T&& b)
{
    return a > b;
}

template<typename T, int N> 
struct make_it<std::array<T, N>, std::enable_if_t<greater(N,4)>>
{
    static std::array<T, N> apply()
    {
        return std::array<T,N>{};  
    }
};

template<typename Type> 
Type make()
{
    return make_it<Type>::apply(); 
}

int main()
{
    auto a = make<std::array<double,3>>(); 
    auto b = make<std::array<double,4>>(); 
    auto c = make<std::array<double,5>>();
}

Скомпилировано с

g++  -O3 -std=c++2a -Wall -Wpedantic -Wunused-parameter -I /usr/include main.cpp -o main

с использованием g++ (GCC) 8.2.1 20181127это приводит к ошибке

main.cpp: In instantiation of ‘Type make() [with Type = std::array<double, 5>]’:
main.cpp:49:41:   required from here
main.cpp:42:32: error: incomplete type ‘make_it<std::array<double, 5>, void>’ used in nested name specifier
     return make_it<Type>::apply();

Что-то не так с линией SFINAE

struct make_it<std::array<T, N>, std::enable_if_t<greater(N,4)>>

Я подумал, что это должно частично специализировать make_it, включить шаблон для всех N > 4.Так что если N == 5, этот шаблон становится "видимым", и он, безусловно, лучше подходит для вызова

auto c = make<std::array<double,5>>();

, чем неполный тип?Что здесь происходит?

Ответы [ 2 ]

0 голосов
/ 21 февраля 2019

Тип вашего шаблона не соответствует типу массива (int против std::size_t).

В качестве альтернативы, когда вы используете C ++ 17, вы можете использовать:

template<typename T, std::size_t N> 
struct make_it<std::array<T, N>>
{
    static_assert(N >= 3); // As you don't provide specialization for those cases.

    static std::array<T, N> apply()
    {
        if constexpr(N == 3) {
            return {{T(0), T(0), T(0)}};
        } else if constexpr(N == 4) {
            return {{T(0), T(0), T(0), T(0)}};
        } else if constexpr(N > 4) {
            return {};
        }
    }
};
0 голосов
/ 21 февраля 2019

Я не знаю, кто прав (g ++, который выдает ошибку, или clang ++, который компилирует), но я вижу, что в вашем коде есть несовершенство: вы перехватываете размер std::array, то есть std::size_t, поэтомуцелое число без знака, как int, целое число со знаком.

Если вы пишете частичную специализацию, перехватывающую значение правильного типа, std::size_t,

// ..................VVVVVVVVVVV  (not int)
template<typename T, std::size_t N> 
struct make_it<std::array<T, N>, std::enable_if_t<(N > 4)>>
{
    static std::array<T, N> apply()
    {
        return std::array<T,N>{};  
    }
};

или также какauto, если вы можете использовать C ++ 17,

// ..................VVVV
template<typename T, auto N> 
struct make_it<std::array<T, N>, std::enable_if_t<(N > 4)>>
{
    static std::array<T, N> apply()
    {
        return std::array<T,N>{};  
    }
};

, вы увидите, что ваш код компилируется с обоими компиляторами.

...