неявное создание экземпляра неопределенного шаблона при создании псевдонима спецификации частичного шаблона

Я следую примерам шаблонов из книги «Практическое метапрограммирование C++» и достиг части примера, где я не могу заставить код компилироваться без обхода псевдонима. При использовании псевдонима make_tuple_of_derefed_params_t я получаю сообщение об ошибке компилятора "неявное создание экземпляра неопределенного шаблона", когда он был определен. Я могу вызвать его напрямую, используя специализацию частичного шаблона make_tuple_of_derefed_params, но не с псевдонимом. Есть ли что-то еще, что мне нужно сделать? Я получаю сообщение об ошибке как в clang++, так и в g++.

 template <typename F>
    class make_tuple_of_derefed_params;

template <typename Ret, typename... Args>
    struct make_tuple_of_derefed_params<Ret (Args...)>
{
    using type = std::tuple<std::remove_pointer_t<Args>...>;
};

template <typename F>
using make_tuple_of_derefed_params_t = typename make_tuple_of_derefed_params<F>::type;

Полный код:

#include <numeric>
#include <iostream>
#include <type_traits>
#include <tuple>
#include <utility>
#include <time.h>

void adjust_values(double * alpha1,double * beta1,double * alpha2,double * beta2) { }

struct location {
    int x;
    int y;
};

class reading
{
    /* stuff */
    public:
    double alpha_value(location l, time_t t) const { return 1.5; }
    double beta_value(location l, time_t t) const { return 2.5; }
    /* other stuff */
};

 template <typename F>
    class make_tuple_of_derefed_params;

template <typename Ret, typename... Args>
    struct make_tuple_of_derefed_params<Ret (Args...)>
{
    using type = std::tuple<std::remove_pointer_t<Args>...>;
};

template <typename F>
using make_tuple_of_derefed_params_t = typename make_tuple_of_derefed_params<F>::type;

template <std::size_t FunctionIndex,typename FunctionsTuple,
        typename Params, std::size_t... I>
    auto dispatch_params(FunctionsTuple & functions,Params & params,
        std::index_sequence<I...>)
{
    return (std::get<FunctionIndex>(functions))(std::get<I>(params)...);
}

template <typename FunctionsTuple, std::size_t... I, typename Params,
            typename ParamsSeq>
    auto dispatch_functions(FunctionsTuple & functions,
            std::index_sequence<I...>, Params & params,
            ParamsSeq params_seq)
{
    return std::make_tuple(dispatch_params<I>(functions,params,params_seq)...);
}

template <typename LegacyFunction,typename... Functions,typename... Params>
    auto magic_wand(
        LegacyFunction legacy,
        const std::tuple<Functions...> & functions,
        const std::tuple<Params...> & params1,
        const std::tuple<Params...> & params2)
{
    static const std::size_t functions_count = sizeof...(Functions);
    static const std::size_t params_count = sizeof...(Params);

    make_tuple_of_derefed_params_t<LegacyFunction> params =

    std::tuple_cat(
        dispatch_functions(functions,
            std::make_index_sequence<functions_count>(),
            params1,
            std::make_index_sequence<params_count>()),
        dispatch_functions(functions,
            std::make_index_sequence<functions_count>(),
            params2,
            std::make_index_sequence<params_count>()));
    /* rest of the code */
    static constexpr auto t_count = 
        std::tuple_size<decltype(params)>::value;

    dispatch_to_c(  legacy, 
                    params,std::make_index_sequence<t_count>());
    return params;
}

template <typename Reading>
    std::tuple<double, double, double, double>
        get_adjusted_values(Reading & r,
            location l,
            time_t t1,
            time_t t2)
{
    return magic_wand(adjust_values,
                std::make_tuple(
                    [&r](location l, time_t t)
                    {
                        return r.alpha_value(l, t);
                    },
                    [&r](location l, time_t t)
                    {
                        return r.beta_value(l, t);
                    }),
                std::make_tuple(l, t1),
                std::make_tuple(l, t2)
            );
}


int main()
{
    reading r;
    location l { 1,2 };
    time_t epoch = 0;
    time_t seconds = time(NULL);

    std::tuple<double, double, double, double> ret2 = 
        get_adjusted_values(r, l, epoch, seconds);

    return 0;
}

person David Ryan    schedule 06.05.2017    source источник
comment
Код в вашем вопросе отлично компилируется как с clang, так и с g++, даже при добавлении тривиального кода для фактического использования вашего псевдонима. Пожалуйста, опубликуйте какой-нибудь реальный код, демонстрирующий проблему, с которой вам нужна помощь.   -  person    schedule 06.05.2017
comment
Вы должны создать экземпляр, чтобы получить ошибку. Я выложил полный исходный код.   -  person David Ryan    schedule 06.05.2017
comment
Я успешно его создал. Обратите внимание, что ошибка, которую вы получаете с вашим кодом, error: implicit instantiation of undefined template 'make_tuple_of_derefed_params<void (*)(double *, double *, double *, double *)>' - вы создаете его экземпляр с типом указателя на функцию, но вы определили его только для типов функций.   -  person    schedule 06.05.2017


Ответы (2)


make_tuple_of_derefed_params определяется только для типов функций.

template <typename F>
class make_tuple_of_derefed_params;

// Definition here, note that it only defines type for "T(Ts...)" template parameters.
template <typename Ret, typename... Args>
struct make_tuple_of_derefed_params<Ret (Args...)>
{
    using type = std::tuple<std::remove_pointer_t<Args>...>;
};

template <typename F>
using make_tuple_of_derefed_params_t = typename make_tuple_of_derefed_params<F>::type;

// ...

//make_tuple_of_derefed_params_t<int> foo;         // Error if uncommented.
make_tuple_of_derefed_params_t<int(int)> bar;      // Works.
//make_tuple_of_derefed_params_t<int(*)(int)> baz; // Error if uncommented.

Чтобы исправить это, вам нужно предоставить определение для любых других допустимых F. В этом конкретном случае вам необходимо указать, когда F является указателем на функцию.

template <typename Ret, typename... Args>
struct make_tuple_of_derefed_params<Ret (*)(Args...)>
{
    using type = std::tuple<std::remove_pointer_t<Args>...>;
};
person Justin Time - Reinstate Monica    schedule 06.05.2017
comment
Тем не менее, dispatch_to_c не определено в вашем расширенном коде; Я подозреваю, что это была часть остального кода, которую вы для краткости пропустили. - person Justin Time - Reinstate Monica; 06.05.2017
comment
Большое Вам спасибо. Работает хорошо. Я пропустил 'Ret (*) (Args...). - person David Ryan; 06.05.2017
comment
@DavidRyan Пожалуйста. Также можно добавить поддержку ссылок на функции и функций-членов, если вам это нужно, используя аналогичный синтаксис. - person Justin Time - Reinstate Monica; 10.05.2017

Данный

template <typename T> void f(T);
void g();

Все три вызова f<void()>(g), f<void(*)()>(g) и f<void(&)()>(g) действительны. Действительно, f(g) мог быть любым из них. Но стандарт должен был заставить один, и в итоге это было f<void(*)()>(g). Это означает, что когда вы затем передаете T другому шаблону, либо этот другой шаблон должен иметь возможность работать с типами указателя на функцию, либо вам нужно превратить свой тип указателя на функцию в тип функции.

Вот что происходит с вашим return magic_wand(adjust_values, ...) звонком.

person Community    schedule 06.05.2017