Search code examples
c++c++17metaprogrammingtemplate-meta-programming

C++: static assert that functor's argument is const reference


I'm writing a template function in C++17 which accepts a functor F as argument and I want to restrict the passed in functor to have only one constant reference argument, where T can be any type.

for example:

template <class T> struct my_struct{
    std::vector<T> collection;
    template <class F> std::vector<T> func(F f){
        static_assert(
               // CONDITION HERE!,
               "f's argument is not const reference"
            );

        std::vector<T> ret;

        std::copy_if(
                std::make_move_iterator(this->collection.begin()),
                std::make_move_iterator(this->collection.end()),
                std::inserter(ret, ret.end()),
                f
            );

        return ret;
    }
};

Obviously, in case f is [](auto v){return true;} the resulting vector returned from func will have empty elements (because those are moved before adding to resulting container). So, I need to restrict possible input functors to [](const auto& v){}.

I have tried something like this:

static_assert(
        std::is_invocable_v<F, const T&>,
        "f's argument is not const reference"
    );

But then func([](auto v){}) does not trigger the assertion, because T is copyable in my case. The func([](auto& v){}) also passes the test, because auto can be const T.

But I need to limit possible lambdas to be func([](const auto& v){}).


Solution

  • I finally managed to achieve it in the following way:

    
    template <class T> struct my_struct{
        std::vector<T> collection;
    
        struct noncopyable_value_type : T{
            noncopyable_value_type(const noncopyable_value_type&) = delete;
            noncopyable_value_type& operator=(const noncopyable_value_type&) = delete;
        };
    
        template <class F> std::vector<T> func(F f){
            static_assert(
                   std::is_invocable_v<F, noncopyable_value_type>,
                   "f's argument must be const reference"
                );
    
            std::vector<T> ret;
    
            std::copy_if(
                    std::make_move_iterator(this->collection.begin()),
                    std::make_move_iterator(this->collection.end()),
                    std::inserter(ret, ret.end()),
                    f
                );
    
            return ret;
        }
    };
    
    

    But still, the problem here is that it only works with generic lambdas.