Проверьте, совпадает ли тип возврата функции со значением типа контейнера STL - PullRequest
0 голосов
/ 28 октября 2018

Я работаю со структурой, которая принимает универсальную функцию и универсальный контейнер STL, но я хочу сделать проверку типа в конструкторе, чтобы вызвать ошибку, если возвращаемый тип функции отличается от конструктора type: возможно ли сделать что-то подобное без изменения шаблона?

template<class Function, class Container>
struct task{
        Function f;
        Container& c;

        task(Function func, Container& cont):f(func), c(cont){
                //error if mismatch between container type and function return type
        }
}; 

int multiply(int x){ return x*10; }

int main(){
        vector<int> v;
        int c=10;
        auto stateless = [](float x){ return x*10;};
        auto stateful = [&c](int x){ return x*c;};

        task t(multiply, v); //SAME TYPE: OKAY!
        task tt(stateless, v); //TYPE MISMATCH: ERROR!

        return 0;
}

спасибо за вашу помощь

Ответы [ 3 ]

0 голосов
/ 28 октября 2018

Я не вижу способа продвинуться без использования какого-либо вспомогательного шаблона для определения списка параметров здесь!

Таким образом, следующее решение все еще основано на Можно ли выяснить тип параметраи возвращаемый тип лямбды?

Для наличия указателей на функции и вызываемых классов, таких как лямбда-выражения, требуется только специальный экземпляр шаблона.

template <typename CLASS>
struct function_traits_impl
: public function_traits_impl<decltype(&CLASS::operator())>
{};

template <typename CLASS, typename RET, typename... ARGS>
struct function_traits_impl< RET(CLASS::*)(ARGS...) const>
{
    using args_type = std::tuple<ARGS...>;
    using ret_type = RET;
};

template <typename CALLABLE > struct function_traits: public    function_traits_impl< CALLABLE >{};

template< typename RET, typename... ARGS >
struct function_traits< RET(*)(ARGS...) >
{
    using args_type = std::tuple<ARGS...>;
    using ret_type = RET;
};


template < typename CLASS, typename CONTAINER, typename RET, typename ... ARGS> struct task;
template< typename CLASS, typename CONTAINER, typename RET, typename ... ARGS >
struct task< CLASS, CONTAINER, RET, std::tuple<ARGS...> >
{
    using FUNC = std::function< RET(ARGS...)>;

    FUNC func;
    CONTAINER cont;

    task(  FUNC _func,  CONTAINER& _cont): func{_func}, cont{_cont}
    {
        static_assert(
            std::is_same<
            //decltype( func( std::declval<PARMS>()...) ), // but is already known from given template parms!
            RET,
            typename CONTAINER::value_type
            >::value,
            "wrong return type, did not match with container type"
            );

    }
};

template <typename FUNC, typename CONTAINER >
task(FUNC, CONTAINER) -> task< FUNC, CONTAINER, typename function_traits<FUNC>::ret_type, typename function_traits<FUNC>::args_type>;



int Any( int ) { return 0; }
float WrongAny( int, int ) { return 1.1; }

int main()
{
    std::vector<int> v;
    //task t1{ [](int, int)->float { return 0; } , v}; // fails with assert as expected
    task t2{ [](int, int)->int { return 0; } , v}; //Works!
    task t3{ &Any , v}; // Works
    //task t4{ &WrongAny, v }; fails as expected
}

Это решение просто использует определяемый пользователем выводруководство по пересылке найденных парм из черты, что полезно, так как вы также используете c ++ 17.

Подсказка: нельзя использовать общие лямбды, потому что, если параметры для вызова лямбды неизвестны, как вы можете определитьпараметры "автоматически".Довольно просто указать параметры с помощью вызова и получить тип возвращаемого значения, но при передаче обобщенной лямбды или объекта с перегруженным оператором вызова необходимо указать, какие из функций / методов следует использовать.Поэтому, если вам нужны общие лямбды или перегруженные методы в объектах классов, просто укажите параметры вручную!Ни в одном языке не может быть хитрости, которая позволяет вам давать набор необязательных вызовов и автоматически определять, какой вызов следует использовать, если другая информация недоступна.Как сказано: если присутствуют параметры для вызова, просто используйте их!

Примечание: если вы используете это решение, вы получите только один экземпляр шаблона для всех вызовов с тем же параметром, установленным для вызова функции, который может сохранитьнемного памяти;) Но он использует функцию std :: для хранения вызываемой функции, которая занимает некоторое время выполнения ... Теперь у вас есть два решения, которые отличаются по результатам, но оба пригодны для использования;)

0 голосов
/ 28 октября 2018

Не совсем понятно, но ... если "универсальная функция" не является обобщенной лямбда или шаблоном operator() в классе / структуре ... вы пометили C ++ 17, чтобы вы могли использовать дедукцию направляющие, чтобы вы могли определить тип, возвращаемый функцией, используя направляющие std::function.

Что-то как

decltype(std::function{std::declval<Function>()})::result_type

Для типа значения контейнера обычно доступен тип value_type.

Итак, определив пару типов using внутри тела структуры, вы можете написать

template <typename F, typename C>
struct task
 {
   using rtype = typename decltype(std::function{std::declval<F>()})::result_type;
   using vtype = typename C::value_type;

   // ...

   task (F func, C & cont) : f{func}, c{cont}
    { static_assert( std::is_same<rtype, vtype>{} );}
 }; 

Но обратите внимание, что static_assert() внутри конструктора использует только элементы, которые не являются специфичными для конструктора.

Таким образом, если вам нужно разработать (на примере) десять конструкторов, вы должны написать десять раз одинаковые static_assert() внутри десяти тел конструкторов.

Я предлагаю разместить static_assert() внутри тела структуры, так что вы должны написать его только один раз.

Я имею в виду

template <typename F, typename C>
struct task
 {
   using rtype = typename decltype(std::function{std::declval<F>()})::result_type;
   using vtype = typename C::value_type;

   static_assert( std::is_same<rtype, vtype>{} );

   // ...
 }; 

Ниже приведен полный пример компиляции

#include <vector>
#include <functional>

template <typename F, typename C>
struct task
 {
   using rtype = typename decltype(std::function{std::declval<F>()})::result_type;
   using vtype = typename C::value_type;

   static_assert( std::is_same<rtype, vtype>{} );

   F   f;
   C & c;

   task (F func, C & cont) : f{func}, c{cont}
    { }
 }; 

int multiply (int x)
 { return x*10; }

int main ()
 {
   std::vector<int> v;

   int c=10;

   auto stateless = [](float x){ return x*10;};
   auto stateful  = [&c](int x){ return x*c;};

   task t1(multiply, v);  // compile
   task t2(stateful, v);  // compile
   task t3(stateless, v); // compilation error
 }

Но помните: эта функция не работает с generic-лямбдами.

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

0 голосов
/ 28 октября 2018

Вы можете использовать static_assert с std::is_same для проверки равенства типов во время компиляции.

Если ваша лямбда-функция всегда не принимает параметров, вы можете использовать decltype(f()), чтобы получитьтип возвращаемого значения функции, в противном случае вам потребуется std::result_of / std::invoke_result или реализация признаков функции .

#include <type_traits>

template<class Function, class Container>
struct task{
        Function f;
        Container& c;

        task(Function func, Container& cont):f(func), c(cont){
                static_assert(
                        std::is_same<
                                decltype(f()),                 // type of function return value
                                typename Container::value_type // type of values stored in container
                        >::value,
                        "incompatible function" // error message
                );
        }
};
...