CRTP类中成员函数的可见性

时间:2015-11-11 19:01:25

标签: c++ templates language-lawyer c++14 crtp

我正在编写一个带有分拣机功能对象的排序库。其中一个主要类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 ++中滥用了不同的编译器特定行为,并且实现了一些无法工作的东西,但仍然......我很惊讶它的工作原理,即使在我的整个项目中也是如此还有许多棘手的事情要处理......

1 个答案:

答案 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。因此,在尝试确定返回类型之前,必须等到实际使用它。