如何实现构造函数,以便它只接受使用typeid的输入迭代器?

时间:2017-08-08 09:45:35

标签: c++ constructor iterator c++14 sfinae

我想为某个对象实现一个范围构造函数,但我想将它限制为只接受两个输入迭代器。

我尝试使用gcc 7.1.0编译此代码。

档案test.cpp

#include <vector>
#include <type_traits>
#include <typeinfo>

template <typename Iterator>
using traits = typename std::iterator_traits<Iterator>::iterator_category;

template <typename T>
class A{
   private:

      std::vector<T> v;

   public:

      template <typename InputIterator,
               typename = std::enable_if_t<
                  typeid(traits<InputIterator>) ==
                  typeid(std::input_iterator_tag)>
               >
      A(InputIterator first, InputIterator last) : v(first, last) {}
};

int main(){
   std::vector<double> v = {1, 2, 3, 4, 5};
   A<double> a(v.begin(), v.end());
}

我在g++ test.cpp -o test

时收到此编译错误
  test.cpp: In function ‘int main()’:
  test.cpp:27:34: error: no matching function for call to ‘A<double>::A(std::vector<double>::iterator, std::vector<double>::iterator)’
      A<double> a(v.begin(), v.end());
                                    ^
  test.cpp:22:7: note: candidate: template<class InputIterator, class> A<T>::A(InputIterator, InputIterator)
         A(InputIterator first, InputIterator last) : v(first, last) {}
         ^
  test.cpp:22:7: note:   template argument deduction/substitution failed:
  test.cpp: In substitution of ‘template<bool _Cond, class _Tp> using enable_if_t = typename std::enable_if::type [with bool _Cond = ((const std::type_info*)(& _ZTISt26random_access_iterator_tag))->std::type_info::operator==(_ZTISt18input_iterator_tag); _Tp = void]’:
  test.cpp:18:16:   required from here
  test.cpp:19:49: error: call to non-constexpr function ‘bool std::type_info::operator==(const std::type_info&) const’
                     typeid(traits<InputIterator>) ==
  test.cpp:18:16: note: in template argument for type ‘bool’ 
                  typename = std::enable_if_t<
                  ^~~~~~~~
  test.cpp:10:7: note: candidate: A<double>::A(const A<double>&)
   class A{
         ^
  test.cpp:10:7: note:   candidate expects 1 argument, 2 provided
  test.cpp:10:7: note: candidate: A<double>::A(A<double>&&)
  test.cpp:10:7: note:   candidate expects 1 argument, 2 provided

我决定使用默认模板参数,因为它更适合构造函数。运算符typeid()的使用是因为我发现在保存代码时它很容易阅读,但我无法以任何方式使它工作。

其他解决方案看起来非常奇怪并且非常模糊(比如强制InputIterator参数使用某些方法,例如* it或++)。如果我无法做到这一点,我会很感激或多或少易于阅读的解决方案。

2 个答案:

答案 0 :(得分:3)

为了做SFINAE,您需要确保在编译时对涉及表达式的评估进行。对于typeid,以下内容适用:

  

当应用于多态类型的表达式时,评估a   typeid表达式可能涉及运行时开销(虚拟表   查找),否则在编译时解析typeid表达式。

因此,我不认为typeid是静态(编译时)多态的好选择。

解决问题的一种方法是将标记调度与委托开发人员结合使用,如下所示:

template <typename T>
class A{
  std::vector<T> v;

  template <typename InputIterator>
  A(InputIterator first, InputIterator last, std::input_iterator_tag) : v(first, last) {}

public:

  template<typename InputIterator> A(InputIterator first, InputIterator last)
  : A(first, last, typename std::iterator_traits<InputIterator>::iterator_category()) {}
};

Live Demo

答案 1 :(得分:1)

  

运算符typeid()的使用是因为我发现在保存代码时它很容易阅读,但我不能让它以任何方式工作。

typeid是一个主要用于在运行时查询类型信息的工具。虽然您可能会发现它更具“可读性”,但它并不适合这项工作,它会让所有其他C ++开发人员感到困惑。

  

其他解决方案看起来非常奇怪且非常模糊(比如强制InputIterator参数使用某些方法,例如*it++it

我强烈建议你重新考虑这一点。一个InputIterator is a concept,用于描述对类型的有效操作。概念背后的整个想法是检查操作有效性 - 只有InputIterator而不是its requirements

解决问题的正确和惯用方法是创建constexpr bool is_input_iterator<T>变量/函数,如果true符合T的要求,则返回InputIterator。您可以使用detection idiom轻松实现该功能......并且它不会比std::enable_if_t<is_input_iterator<T>>更具可读性。

这是一个简化的例子(我没有检查所有要求)

template <typename T>
using supports_increment = decltype(++std::declval<T>());

template <typename T>
using supports_dereference = decltype(*std::declval<T>());

template <typename T>
constexpr bool is_input_iterator = 
    std::experimental::is_detected_v<supports_increment, T> 
 && std::experimental::is_detected_v<supports_dereference, T>;

  template <typename InputIterator,
           typename = std::enable_if_t<is_input_iterator<InputIterator>>>
  A(InputIterator first, InputIterator last) : v(first, last) {}

live example on wandbox