【发布时间】:2015-11-11 19:01:25
【问题描述】:
我正在编写一个带有排序函数对象的排序库。主要类之一sorter_facade 旨在根据已经存在的重载向分拣机提供operator() 的一些重载。下面是一个简单的 heap_sorter 对象的简化示例,它实现了一个堆排序:
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++ 不会评估“相同”的@987654344 @ 检查是否存在重载时。
对于这个简单的例子,删除 std::enable_if_t 可以使整个事情正常工作,但是 sorter_facade 类实际上比这要大得多,我需要它来解决 operator() 的其他重载的歧义问题,所以只需删除这不是一个解决方案。
那么...是什么导致了错误?编译器应该接受还是拒绝这个代码?最后,是否有一种标准兼容的方式可以使其与最新版本的 g++ 和 clang++ 一起工作?
编辑: 作为旁注,我通过添加另一层黑魔法,设法让所有疯狂的人都可以使用 g++5 和 clang++3.8 > 以至于我完全不知道为什么它甚至不再起作用了。虽然前面的所有问题都成立,但这里是“解决方法”(使用 C++17 std::void_t):
tempate<typename Sorter>
struct wrapper:
Sorter
{
#ifdef __clang__
using Sorter::operator();
template<typename Iterable>
auto operator()(Iterable& iterable) const
-> std::enable_if_t<false, std::void_t<Iterable>>
{}
#endif
};
template<typename Sorter>
struct sorter_facade
{
template<typename Iterable>
auto operator()(Iterable& iterable) const
-> std::enable_if_t<
not has_sort<wrapper<Sorter>, Iterable>,
decltype(std::declval<Sorter&>()(std::begin(iterable), std::end(iterable)))
>
{
return Sorter{}(std::begin(iterable), std::end(iterable));
}
};
我猜它在 g++ 和 clang++ 中滥用了不同的编译器特定的行为,并实现了一些本来不应该工作的东西,但仍然......我很惊讶它工作,即使在我的整个项目中,它还有很多棘手的事情要处理...
【问题讨论】:
-
对不起,我缺乏知识,但是
not has_sort<Sorter, Iterable>在模板中是什么意思?我的意思是,我知道 not 在英语中的含义以及 has_sort 是什么意思,但它是作为模板参数的合法声明吗?我以前从未见过它,我很好奇。 -
@skypjack 有趣的是,
notis a synonym for!in C++ -
@skypjack 当然,这是语言中经常被遗忘的角落。不过,我不会因为它而退出——如果你现在退出,你就无法利用知道它的机会。 ;)
-
我不知道是否有任何规则涵盖了这种“自引用”功能模板。但我倾向于说这是未定义的行为。它甚至让我难以思考 GCC 是如何解释这一点的。你能解释一下吗?
-
看起来像另一个“如果我不存在我就存在”的东西。比较stackoverflow.com/questions/32997699/…
标签: c++ templates language-lawyer c++14 crtp