gpt4 book ai didi

c++ - 以不同方式分派(dispatch)右值和左值并使用 sfinae 禁用一个选项

转载 作者:可可西里 更新时间:2023-11-01 17:58:14 26 4
gpt4 key购买 nike

我想实现一个函数 drop_if .给定一个一元谓词和一个顺序容器,它返回一个相同类型的容器,其中仅包含原始元素中不满足谓词的元素。

如果输入容器是右值,它应该就地工作,否则创建一个拷贝。这是通过调度到 namespace internal 中的适当版本来实现的。 .如果 value_type 应该禁用 r 值版本容器的名称不能被覆盖 - 如 std::pair<const int, int>例如 - 即使容器是右值。

以下代码works as expected使用 clang 和当前版本的 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
}

但是它确实如此not compileMSVC++GCC 6.2 ,因为它们对 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++

请参阅 this question 的答案和 Library Defect Report 2729 .

我希望它适用于不同的容器和不同种类的对象,例如std::vector<double> , std::map<int, std::string>等等 std::map案例(使用 different inserter )是我遇到 value_types 问题的情况的 std::pair<const T, U> .

您是否知道如何更改 dispatch/sfinae 以使其也适用于 MSVC++(ver. MSVC++ 2017 15.2 26430.6 在我的例子中)和向下的 GCC 6.2?

最佳答案

问题似乎是 MSVC 的 std::pair<const T, U>::operator=没有禁用 SFINAE。即使实例化它不起作用,它也存在。

所以当你检测它是否存在时,它就存在。如果执行它,它会编译失败。

我们可以解决这个问题。但这是一种解决方法。

namespace internal
{
template <typename Pred, typename Container>
Container drop_if( std::true_type reuse_container, 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::forward<Container>( xs );
}

template <typename Pred, typename Container>
Container drop_if( std::false_type reuse_container, 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<bool b>
using bool_k = std::integral_constant<bool, b>;

template<class T>
struct can_self_assign {
using type = std::is_assignable<T&, T>;
};

template<class T>
using can_self_assign_t = typename can_self_assign<T>::type;

template<class T0, class T1>
struct can_self_assign<std::pair<T0, T1>>
{
enum { t0 = can_self_assign_t<T0>::value, t1 = can_self_assign_t<T1>::value, x = t0&&t1 };
using type = bool_k< x >;
};

template<>
struct can_self_assign<std::tuple<>>
{
using type = bool_k< true >;
};
template<class T0, class...Ts>
struct can_self_assign<std::tuple<T0, Ts...>>
{
using type = bool_k< can_self_assign_t<T0>::value && can_self_assign_t<std::tuple<Ts...>>::value >;
};


template <typename Pred, typename Container,
typename Out = typename std::remove_reference<Container>::type>
Out drop_if( Pred pred, Container&& xs )
{
using dContainer = typename std::decay<Container>::type;
using can_assign = can_self_assign_t<typename dContainer::value_type>;
using cannot_reuse = std::is_lvalue_reference<Container>;

using reuse = std::integral_constant<bool, can_assign::value && !cannot_reuse::value >;

return internal::drop_if( reuse{}, pred, std::forward<Container>( xs ) );
}

live exampleother live example .

我还将您的 SFINAE 调度更改为基于标签的调度。

其他有缺陷的类型operator=禁用可能还需要 can_self_assign专长。值得注意的例子可能包括 tuple<Ts...>vector<T,A>和类似的。

我不知道编译器何时以及是否需要 operator=如果它在 std 中不起作用,则“不存在”类型;我记得有一次不需要 std::vector , 但我还记得有一个提案添加了这样的要求。

关于c++ - 以不同方式分派(dispatch)右值和左值并使用 sfinae 禁用一个选项,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44542512/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com