【问题标题】:How to allow only iterators with a ceratin value_type?如何只允许具有特定 value_type 的迭代器?
【发布时间】:2018-03-19 08:42:54
【问题描述】:

我想编写一个将一对迭代器作为构造函数参数的类,但是当这些迭代器的value_type 与预期类型不匹配时,我不知道如何在编译时引发错误。这是我尝试使用typeid:

#include <vector>

struct foo {
    std::vector<double> data;
    template <typename IT>
    foo(IT begin, IT end){
        typedef int static_assert_valuetype_is_double[
               typeid(typename IT::value_type) == typeid(double) ? 1 : -1
        ];
        std::cout << "constructor called \n";
        data = std::vector<double>(begin,end);
    }    
};

int main()
{
    std::vector<double> x(5);
    foo f(x.begin(),x.end()); // double: ok

    std::vector<int> y(10);
    foo g(y.begin(),y.end()); // int: should not compile
}

请注意,在这种情况下,intdouble 可以,但这只是一个示例,在实际代码中,类型必须完全匹配。令我惊讶的是,在这两种情况下,构造函数都没有错误(只有关于未使用的 typedef 的警告)。当 typedef 在方法中声明时,-1 大小的数组静态断言技巧是否不起作用? IT::value_type 是错误类型时如何产生错误?

PS:如果有一个简单的 C++98 解决方案会很好,但如果太复杂,我也可以接受 C++11 解决方案。

【问题讨论】:

  • double* 是一个有效的迭代器,其值类型显然是double,但double* 没有嵌套的double*::value_type。我想你需要typeid(*begin)
  • @MSalters 抱歉,如果我的问题不清楚。我主要关心标准容器及其迭代器。如果一个解决方案排除了一些奇异的迭代器,那不是问题
  • 嗯,指针并不陌生,它们是数组的自然迭代器。见std::begin(MyArray)
  • 这并没有解决问题,但构造函数应该使用初始化列表,而不是为构造函数主体中的(默认构造的)向量分配新值。所以:foo(IT begin, IT end) : data(begin, end) { /* static assert goes here */ }。一般来说,除非有充分的理由延迟设置正确的值,否则对所有成员使用初始化列表。
  • @PeterBecker 我通常总是这样做,不知道为什么我没有在这里这样做。实际上,如果构造函数的主体与 {} 不同,我会感到紧张

标签: c++ iterator typechecking c++98 static-assert


【解决方案1】:

在现代 C++ 中,您可以使用 std::is_samestatic_assert

static_assert(std::is_same_v<typename std::iterator_traits<IT>::value_type, double>,
     "wrong iterator");

另请参阅std::iterator_traits:迭代器it 不保证具有value_type 类型定义,而应使用std::iterator_traits&lt;it&gt;::value_type

在 C++ 98 中,is_same 实现起来很简单,static_assert 需要 negative-size array trickBOOST_STATIC_ASSERT

【讨论】:

  • 可能想展示如何实现它,因为它被标记为 C++98。或提及提升。
  • @StoryTeller is_same 不是 C++98 中的问题,我想我可以解决这个问题,但我的 c++98 静态断言似乎失败了
  • @StoryTeller 是的,但是……我已经好几年没有在 c++98 中做 static_assert-ike 了:/
  • @user463035818 - 失败怎么办?我认为您的版本失败是因为typeid
  • @user463035818 is_same_v 被定义为 is_same&lt;...&gt;::value ;) 大大节省了打字时间。
【解决方案2】:

适用于 C++98 及更高版本的解决方案.....

#include <iterator>

template<class T> struct TypeChecker
{};

template<> struct TypeChecker<double>
{
     typedef double valid_type;
};

template <typename IT>
    void foo(IT begin, IT end)
{
      typename TypeChecker<typename std::iterator_traits<IT>::value_type>::valid_type type_checker;
      (void)type_checker;

        // whatever
}

对于value_typedouble 的迭代器,foo() 的实例化将成功,否则无法编译。

前提是TypeChecker&lt;x&gt;除了double之外没有任何xvalid_type,但我们尝试在foo()中实例化该类型的实例。 (void)type_checker 可防止某些编译器针对有效类型发出关于从未使用过的变量的警告。

【讨论】:

    【解决方案3】:

    这是一种符合 C++98 的实现方式.....

    首先是有趣的部分:实现is_same 相当简单

    template <typename T,typename U> struct is_same_type { static const bool value; };
    template <typename T,typename U> const bool is_same_type<T,U>::value = false;
    template <typename T> struct is_same_type<T,T> { static const bool value; };
    template <typename T> const bool is_same_type<T,T>::value = true;
    

    现在是不那么有趣的部分(C++11 确实有助于静态断言而不会引起同事的注意):

    struct foo {
        std::vector<double> data;
        template <typename IT>
        foo(IT begin, IT end) : data(begin,end) {
            typedef int static_assert_valuetype_is_double[
                 is_same_type<double,typename IT::value_type>::value ? 1 : -1
            ];
            std::cout << "constructor called \n";
        }
    
    };
    
    int main(){
        std::vector<double> x(5,2.3);
        foo f(x.begin(),x.end());
        for (std::vector<double>::iterator it = f.data.begin(); it != f.data.end();++it) std::cout << *it << " ";
        //std::vector<int> y(10,3);
        //foo g(y.begin(),y.end());  // THIS FAILS (AS EXPECTED)        
    }
    

    正如其他人所指出的,我实际上应该使用std::iterator_traits&lt;IT&gt;::value_type,因为并非每个迭代器都有value_type。但是,在我的情况下,我宁愿将可能的迭代器限制在一个小集合中,并且在我的具体情况下,不允许没有 value_type 的迭代器不是问题。

    还要注意问题中的代码分配给成员,当然最好使用初始化列表。

    【讨论】:

      猜你喜欢
      • 2016-08-25
      • 2019-05-05
      • 2018-11-16
      • 1970-01-01
      • 1970-01-01
      • 2021-05-02
      • 2012-06-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多