I would like to implement a function drop_if. Given a unary predicate and a sequential container, it returns a container of the same type holding only the elements from the original one not fulfilling the predicate.
If the input container is an r-value it should work in-place otherwise create a copy. This is achieved by dispatching to the appropriate version in namespace internal. The r-value version should be disabled if the value_type of the container can not be overwritten - like std::pair<const int, int> for example - even if the container is an r-value.
The following code works as expected with clang and current versions of gcc (>=6.3).
#include <algorithm>
#include <iostream>
#include <iterator>
#include <type_traits>
#include <utility>
#include <vector>
namespace internal
{
    template <typename Pred, typename Container,
        typename = typename std::enable_if<
        std::is_assignable<
            typename Container::value_type&,
            typename Container::value_type>::value>::type>
    Container drop_if( Pred pred, Container&& xs )
    {
        std::cout << "r-value" << std::endl;
        xs.erase( std::remove_if( std::begin( xs ), std::end( xs ), pred ), std::end( xs ) );
        return std::move( xs );
    }
    template <typename Pred, typename Container>
    Container drop_if( Pred pred, const Container& xs )
    {
        std::cout << "l-value" << std::endl;
        Container result;
        auto it = std::back_inserter( result );
        std::remove_copy_if( std::begin( xs ), std::end( xs ), it, pred );
        return result;
    }
} // namespace internal
template <typename Pred, typename Container,
    typename Out = typename std::remove_reference<Container>::type>
    Out drop_if( Pred pred, Container&& xs )
{
    return std::move( internal::drop_if( pred, std::forward<decltype(xs)>( xs ) ) );
}
typedef std::pair<int, int> pair_t;
typedef std::vector<pair_t> vec_t;
bool sum_is_even( pair_t p )
{
    return (p.first + p.second) % 2 == 0;
}
typedef std::pair<const int, int> pair_c_t;
typedef std::vector<pair_c_t> vec_c_t;
bool sum_is_even_c( pair_c_t p)
{
    return (p.first + p.second) % 2 == 0;
}
int main()
{
    vec_c_t v_c;
    drop_if( sum_is_even_c, v_c ); // l-value
    drop_if( sum_is_even_c, vec_c_t() ); // l-value
    vec_t v;
    drop_if( sum_is_even, v ); // l-value
    drop_if( sum_is_even, vec_t() ); // r-value
}
However it does not compile on MSVC++ and GCC 6.2, because they behave incorrectly for std::is_assignable:
using T = std::pair<const int, int>;
const auto ok = std::is_assignable<T&, T>::value;
// ok == true on GCC 6.2 and MSVC++
See answer to this question and Library Defect Report 2729.
I would like it to work with different containers and with different kinds of objects, e.g. std::vector<double>, std::map<int, std::string> etc. The std::map case (using a different inserter) is the situation in which I encountered the problem with value_types of std::pair<const T, U>.
Do you have any ideas how the dispatch / sfinae could be changed to also work for MSVC++ (ver. MSVC++ 2017 15.2 26430.6 in my case) and for GCC 6.2 downwards?
 
     
     
     
    