如何仅允许带有 ceratin value_type的迭代器

How to allow only iterators with a ceratin value_type?

本文关键字:type 迭代器 value ceratin 何仅允 许带      更新时间:2023-10-16

我想编写一个将一对迭代器作为构造函数参数的类,但是当这些迭代器的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
}

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

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

在现代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 typedef,应该改用std::iterator_traits<it>::value_type

在 C++ 98 中,实现起来is_same微不足道,static_assert需要一个负大小的数组技巧或BOOST_STATIC_ASSERT

对于在 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_type double的迭代器,foo()的实例化将成功,否则无法编译。

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

这是实现它的 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<IT>::value_type因为并非每个迭代器都有value_type。但是,就我而言,我宁愿将可能的迭代器限制为一小组,在我的特定情况下,不允许没有value_type的迭代器不是问题。

还要注意的是,问题中的代码分配给成员,而使用初始值设定项列表当然更好。