CRTP类中成员函数的可见性

Mor*_*enn 5 c++ templates crtp language-lawyer c++14

我正在编写一个带有分拣机功能对象的分类库.其中一个主要类别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);
    }
};
Run Code Online (Sandbox Code Playgroud)

最简单的目标之一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));
    }
};
Run Code Online (Sandbox Code Playgroud)

在这个类中,has_sort是一个特征,用于检测分拣机是否有operator()过载Iterable&.它是使用检测成语的手动版本实现的:

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>;
Run Code Online (Sandbox Code Playgroud)

现在,针对实际问题:以下内容main适用于g ++ 5.2:

int main()
{
    std::vector<int> vec(3);
    heap_sorter{}(vec);
}
Run Code Online (Sandbox Code Playgroud)

但是,它与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.
Run Code Online (Sandbox Code Playgroud)

显然,在评估时std::enable_if_t,似乎Sorter已经考虑到已经operator()能够接受Iterable&,这可能意味着Sorter当检查是否存在重载时,clang ++和g ++不会评估"相同" .

对于这个简单的例子,删除std::enable_if_t使整个事情工作,但该类sorter_facade实际上比这大得多,我需要它来解决其他重载的模糊问题operator(),所以只是删除它不是一个解决方案.

那么...导致错误的原因是什么?编译器是应该接受还是拒绝此代码?最后,是否有一种标准兼容的方法可以使用最新版本的g ++和clang ++?


编辑:作为旁注,我设法让所有的疯狂与g ++ 5和clang ++ 3.8一起工作,通过添加另一层黑魔法,我不知道为什么它甚至可以工作了.虽然以前的所有问题都存在,但这里是«workaround»(使用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));
    }
};
Run Code Online (Sandbox Code Playgroud)

我猜它在g ++和clang ++中都滥用了不同的编译器特定行为,并且实现了一些无法工作的东西,但仍然......我很惊讶它的工作原理,即使在我的整个项目中,它还有很多棘手的问题.要处理的事情......

Rum*_*rak 1

我很确定这是 clang 中的一个错误。的返回类型sorter_facade<Sorter>::operator()取决于模板参数 Iterator。尽管如此,编译器似乎在知道参数之前就决定将 SFINAE 排除。

不管有没有错误,您都可以通过显式推迟返回类型的计算来解决这个问题。这是一个不依赖黑魔法的版本。适用于 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);
}
Run Code Online (Sandbox Code Playgroud)

诀窍是:编译器不知道您稍后是否专门化了 result_type。因此,它必须等到您实际使用它,然后才能尝试确定返回类型。