我正在编写一个带有分拣机功能对象的排序库。其中一个主要类sorter_facade
旨在根据已存在的重载向分拣机提供operator()
的一些重载。这是一个heap_sorter
对象的简单缩减示例,实现了一个heapsort:
struct heap_sorter:
sorter_facade<heap_sorter>
{
using sorter_facade<heap_sorter>::operator();
template<typename Iterator>
auto operator()(Iterator first, Iterator last) const
-> void
{
std::make_heap(first, last);
std::sort_heap(first, last);
}
};
sorter_facade
最简单的目标之一是在已经存在一对迭代器的重载时为分拣器的operator()
提供可迭代的重载。以下是sorter_facade
的简化实现,足以解决手头的问题:
template<typename Sorter>
struct sorter_facade
{
template<typename Iterable>
auto operator()(Iterable& iterable) const
-> std::enable_if_t<
not has_sort<Sorter, Iterable>,
decltype(std::declval<Sorter&>()(std::begin(iterable), std::end(iterable)))
>
{
return Sorter{}(std::begin(iterable), std::end(iterable));
}
};
在此课程中,has_sort
是一种特征,用于检测分拣机是否operator()
重载Iterable&
。它是使用detection idiom:
template<typename Sorter, typename Iterable>
using has_sort_t = std::result_of_t<Sorter(Iterable&)>;
template<typename Sorter, typename Iterable>
constexpr bool has_sort = std::experimental::is_detected_v<has_sort_t, Sorter, Iterable>;
现在,针对实际问题:以下main
适用于g ++ 5.2:
int main()
{
std::vector<int> vec(3);
heap_sorter{}(vec);
}
但是,it fails with clang++ 3.7.0,出现以下错误消息:
main.cpp:87:5: error: no matching function for call to object of type 'heap_sorter' heap_sorter{}(vec); ^~~~~~~~~~~~~ /usr/local/bin/../lib/gcc/x86_64-unknown-linux-gnu/5.2.0/../../../../include/c++/5.2.0/type_traits:2388:44: note: candidate template ignored: disabled by 'enable_if' [with Iterable = std::vector<int, std::allocator<int> >] using enable_if_t = typename enable_if<_Cond, _Tp>::type; ^ main.cpp:75:10: note: candidate function template not viable: requires 2 arguments, but 1 was provided auto operator()(Iterator first, Iterator last) const ^ 1 error generated.
显然,在评估std::enable_if_t
时,似乎认为Sorter
已经有operator()
能够接受Iterable&
,这可能意味着clang ++和g ++会这样做在检查是否存在重载时,不要评估“相同”Sorter
。
对于这个简单的例子,删除std::enable_if_t
会使整个过程发挥作用,但是类sorter_facade
实际上要大得多,我需要它来解决{{1}的其他重载的歧义问题只是删除它不是一个解决方案。
那么......导致错误的原因是什么?编译器是应该接受还是拒绝此代码?最后,是否有一种标准兼容的方法可以使用最新版本的g ++和clang ++?
编辑:作为旁注,我通过添加另一层黑魔法到了我不知道为什么它甚至可以工作了。虽然以前的所有问题都存在,但这里是“解决方法”(使用C ++ 17 operator()
):
std::void_t
我猜它在g ++和clang ++中滥用了不同的编译器特定行为,并且实现了一些无法工作的东西,但仍然......我很惊讶它的工作原理,即使在我的整个项目中也是如此还有许多棘手的事情要处理......
答案 0 :(得分:1)
我很确定这是clang中的一个错误。返回类型sorter_facade<Sorter>::operator()
取决于模板参数Iterator。不过,在知道参数之前,编译器似乎决定退出SFINAE。
但是否有bug,你可以通过明确推迟返回类型的计算来解决这个问题。这是一个不依赖于黑魔法的版本。适用于gcc-5.2和clang-3.6:
template<typename Sorter, typename Iterable>
struct sort_result
{
using type = decltype(
std::declval<Sorter&>()(
std::begin(std::declval<Iterable&>()),
std::end(std::declval<Iterable&>())));
};
template<typename Sorter, typename Deferred>
using sort_result_t = typename sort_result<Sorter, Deferred>::type;
template<typename Sorter>
struct sorter_facade
{
template <typename Iterable>
auto operator()(Iterable& iterable) const
-> sort_result_t<Sorter, Iterable>
{
return Sorter{}(std::begin(iterable), std::end(iterable));
}
};
struct heap_sorter:
sorter_facade<heap_sorter>
{
using sorter_facade<heap_sorter>::operator();
template<typename Iterator>
auto operator()(Iterator first, Iterator last) const
-> void
{
std::make_heap(first, last);
std::sort_heap(first, last);
}
};
int main()
{
std::vector<int> vec(3);
heap_sorter{}(vec);
}
诀窍是:编译器不知道你以后是否专门化了result_type。因此,在尝试确定返回类型之前,必须等到实际使用它。