默认模板参数的enable_if迭代器?

5

我有一个像这样的构造函数:

class MyClass
{
    template<class TI> MyClass(TI first, TI last);
};

template<class TI> MyClass::MyClass(TI first, TI last)
{
    ;
}

我希望仅在TI是迭代器时启用此构造函数(这意味着我认为TI具有iterator_category)。如何在C++ 2011中使用enable_if作为默认模板参数(在声明和定义中)来实现?非常感谢。

这个回答解决了你的问题吗?如何检查模板参数是否为迭代器类型? - TingQian LI
1个回答

12

这取决于你想要什么。如果没有其他重载,那就可以完全不用任何东西。如果传递的类型没有提供必要的操作,编译器将产生一个错误。

如果你真的想限制为迭代器,最好使用static_assert来实现,因为它会生成具有良好自定义错误消息的错误,而不是“模棱两可的函数调用,这里是我找到的所有弯道超车: 接下来是无休止的重载列表”或“找不到函数,请自己找”。

如果存在另一个与之冲突的模板重载,那么确实需要一些enable_if的东西。我写了一篇关于如何使用C++11特性的enable_if以及为什么默认模板参数并不是很好的选择的博客文章。我最终采用了类似于以下方式的方法:

enum class enabler {};

template <typename Condition>
using EnableIf = typename std::enable_if<Condition::value, enabler>::type;


class MyClass
{
    template<class TI, EnableIf<is_iterator<TI>>...> MyClass(TI first, TI last);
};

template<class TI, EnableIf<is_iterator<TI>>...> MyClass::MyClass(TI first, TI last)
{ /* blah */ }

现在你所需要的就是一个测试的特性。我认为测试iterator_category的存在已经足够了,但是应该使用std :: iterator_traits来完成,因为指针也是迭代器,但没有嵌套的typedefs。

可以使用通常使用SFINAE的技术来完成此操作。在C++11中,我执行以下操作:

template <typename T>
struct sfinae_true : std::true_type {};

struct is_iterator_tester {
    template <typename T>
    static sfinae_true<typename std::iterator_traits<T>::iterator_category> test(int);

    template <typename>
    static std::false_type test(...);
};

template <typename T>
struct is_iterator : decltype(is_iterator_tester::test<T>(0)) {};

所有这些说法,都可以使用传统的技术来实现,即使用默认函数参数:

class MyClass
{
    template<class TI>
    MyClass(TI first, TI last,
            typename std::iterator_traits<T>::iterator_category* = nullptr)
};

template<class TI>
MyClass::MyClass(TI first, TI last,
                 typename std::iterator_traits<T>::iterator_category*)
{ /* blah */ }

非常感谢您提供这么清晰的总结。您关于默认参数模板可能存在的危险的博客文章非常有趣。 - Vincent
也许是时候写一本关于“现代C ++”的书了吗? - Kerrek SB
请查看此链接:https://dev59.com/NW855IYBdhLWcg3wcz_y 它检查iterator_traits<It>::value_type是否为void。 - TingQian LI

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接