禁止使用iterator参数进行函数模板实例化

时间:2015-07-14 13:41:55

标签: c++ templates visual-c++ c++14 function-templates

我有一个函数模板,它采用模板化参数:

template <class R>
RefT<R> make_ref(R& res) {
    return RefT<R>(&res);
}

我想要阻止R成为任何类型的迭代器,或者,如果这更容易,我希望有一个重载,编译器将更喜欢用于迭代器,再次使用迭代器重新调用make_ref

最好的方法是将两者结合起来,因此编译器更喜欢使用迭代器特定的重载,并拒绝使用非特定版本。

我希望代码的使用者能够调用make_ref(something)而不必考虑something是否是迭代器 - 如果是,我只需要做一些不同的事情,并且如果不可能,请向消费者提供有用的错误消息。

3 个答案:

答案 0 :(得分:4)

首先是特征(你可能需要根据你的要求进行调整):

<property name="name" type="string">
      <meta attribute="use-in-tostring">true</meta>
      <meta attribute="use-in-equals">true</meta>      
</property>

注意:使用template <typename T> auto is_iterator_impl(T* it) -> decltype(**it, ++(*it), (*it) == (*it), std::true_type()); template <typename T> auto is_iterator_impl(...) -> std::false_type; template <typename T> using is_an_iterator = decltype(is_iterator_impl<T>(0)); 可能是一个不错的选择。

使用SFINAE,您可以

std::iterator_traits<IT>

注意:由于您希望以不同方式管理指针,除了自定义template <class R> std::enable_if_t<!is_an_iterator<R>::value, RefT<R>> make_ref(R& res) { return RefT<R>(&res); } template <class R> std::enable_if_t<is_an_iterator<R>::value && !std::is_pointer<R>::value, RefT<R>> // you may want to change return type make_ref(R& res) { // Implementation for iterator } template <class R> std::enable_if_t<std::is_pointer<R>::value, RefT<R>> // you may want to change return type make_ref(R& res) { // Implementation for iterator } 之外,我还使用std::is_pointer

注意:条件不应该重叠,否则就会发生冲突。

Live Demo

答案 1 :(得分:2)

我在这里使用is_iteratorhttps://stackoverflow.com/a/4336298/678093

此traits结构与SFINAE一起使用,仅为非迭代器类型启用make_ref

#include <type_traits>

template<class T>
struct is_iterator
{   
    static T makeT();
    typedef void * twoptrs[2];  // sizeof(twoptrs) > sizeof(void *)
    static twoptrs & test(...); // Common case
    template<class R> static typename R::iterator_category * test(R); // Iterator
    template<class R> static void * test(R *); // Pointer

    static const bool value = sizeof(test(makeT())) == sizeof(void *); 
};


// just to make it compile
template <typename R>
struct RefT{};

template <class R, typename std::enable_if<!is_iterator<R>::value>::type* = nullptr>
RefT<R> make_ref(R& res)
{
    return RefT<R>(&res);
}

int main()
{
    int* a;
    make_ref(a); // fails to compile

    int b;
    make_ref(b); // compiles once RefT is correct
    return 0;
}

alernative解决方案是使用std::iterator_traits

template <class R, typename std::enable_if<std::is_same<typename  std::iterator_traits<R>::value_type, void>::value>::type* = nullptr>
RefT<R> make_ref(R& res)
{
    return RefT<R>(&res);
}

答案 2 :(得分:0)

这也可以通过使用std::iterator_traits的SFINAE来完成,将处理以前答案处理的所有情况(指针和类型具有internal iterator_category typedef)但是:< / p>

  • 无需编写自己的特征(如is_iterator)来执行此操作,或者至少大部分模板机制都封装在iterator_traits

  • 还可以处理具有自己iterator_traits专业化的潜在用户定义迭代器,而不使用泛型iterator_category typedef,不确定这种相关/法律技术是否可行