简体   繁体   English

检查函数返回类型是否与STL容器类型值相同

[英]Check if function return type is the same as an STL container type value

I'm working with a struct that takes a generic function and a generic STL container, but i want to make a type check in the constructor in order to raise an error if the return type of the function is different from the constructor type: is it possible to do something like this without changing the template? 我正在使用一个带有泛型函数和泛型STL容器的结构,但我想在构造函数中进行类型检查,以便在函数的返回类型与构造函数类型不同时引发错误:is可以在不更改模板的情况下执行此类操作吗?

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

thank you for your help 谢谢您的帮助

Not sure to understand completely but... if the "generic funcion" isn't a generic-lambda or a template operator() in a class/struct... you tagged C++17 so you can use deduction guides so you can deduce the type returned from the function using std::function 's deduction guides. 不确定完全理解,但是...如果“泛函”不是类/结构中的泛型lambda或模板operator() ...你标记了C ++ 17所以你可以使用演绎指南这样你可以使用std::function的演绎指南推断出从函数返回的类型。

Something as 有点像

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

For the value type of the container is usually available the value_type type. 对于容器的值类型,通常可以使用value_type类型。

So, defining a couple of using types inside the body of the struct, you can write 因此,在结构体内定义几个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>{} );}
 }; 

But observe that the static_assert() inside the constructor use only elements that aren't specific of the constructor. 但请注意,构造函数中的static_assert()仅使用不是构造函数特定的元素。

This way, if you have to develop (by example) ten constructors, you have to write ten times the same static_assert() inside the ten constructors bodies. 这样,如果必须开发(通过示例)十个构造函数,则必须在十个构造函数体内编写十次相同的static_assert()

I suggest to place the static_assert() inside the body of the struct so you have to write it only one time. 我建议将static_assert()放在struct的主体中,这样你只需要编写一次。

I mean 我的意思是

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

   // ...
 }; 

The following is a full compiling example 以下是完整的编译示例

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

But remember: this function doen't works with generic-lambdas. 但请记住:此函数不适用于generic-lambdas。

In that case I don't know how to solve the problem and I suppose isn't solvable at all without knowing the type of the input parameters. 在那种情况下,我不知道如何解决问题,我想在不知道输入参数的类型的情况下根本无法解决。

You can use static_assert with std::is_same to check type equality at compile time. 您可以将static_assertstd::is_same一起使用,以在编译时检查类型是否相等。

If your lambda function always takes no parameters, you can use decltype(f()) to get the function return type, else you will need std::result_of / std::invoke_result or a function traits implementation . 如果你的lambda函数总是不带参数,你可以使用decltype(f())来获取函数返回类型,否则你需要std::result_of / std::invoke_result函数traits实现

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

I see no way to go ahead without using any kind of helper template to determine the parameter list here! 我没有办法在没有使用任何类型的帮助模板来确定参数列表的情况下继续前进!

So the following solution is still based on Is it possible to figure out the parameter type and return type of a lambda? 所以以下解决方案仍然基于是否有可能找出lambda的参数类型和返回类型?

For having function pointers and callable classes like lambdas, it only needs an specialized template instance. 对于具有函数指针和可调用类(如lambdas),它只需要一个专门的模板实例。

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
}

This solution simply uses user defined deduction guide to forward the found parms from the trait which is helpful as you also use c++17. 这个解决方案只是使用用户定义的演绎指南从特征中转发找到的参数,这也很有用,因为你也使用c ++ 17。

Hint: Generic lambdas cant be used, because if the parameters to call the lambda are unknown, how you could determine the parameters "automatically". 提示:不能使用泛型lambda,因为如果调用lambda的参数未知,那么如何“自动”确定参数。 It is quite easy to specify the parameters with the call and get the return type, but passing an generic lambda or an object with overloaded call operator needs to specify which of the functions/methods are should be used. 使用调用指定参数并获取返回类型非常容易,但是传递泛型lambda或具有重载调用操作符的对象需要指定应该使用哪些函数/方法。 So if you need generic lambdas or overloaded methods in class objects simply specify params manually! 因此,如果您需要在类对象中使用通用lambda或重载方法,只需手动指定params! There can't be a trick in any language which allows you to give a set of optional calls and determine automatically which call should be used if no other information is available. 在任何语言中都没有技巧可以让您提供一组可选的呼叫,并在没有其他可用信息的情况下自动确定应该使用哪个呼叫。 As said: If params for the call are present, simply use them! 如上所述:如果呼叫的参数存在,只需使用它们!

Remark: If you use this solution, you only get a single template instance for all calls with same parameter set to the function call which may save some memory ;) But it uses a std::function to store teh callable which takes some runtime... You have now two solutions which differs in the results but both are usable ;) 备注:如果你使用这个解决方案,你只需要为函数调用设置相同参数的所有调用获得一个模板实例,这可以节省一些内存;)但是它使用std :: function来存储teh callable,这需要一些运行时间。 ..你现在有两个解决方案,结果不同但都可以使用;)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM