如何只允许具有特定 value_type 的迭代器?

How to allow only iterators with a ceratin value_type?

我想写一个 class 将一对迭代器作为构造函数的参数,但我不知道如何在编译时引发错误,因为这些迭代器的 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 解决方案。

在现代 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 需要一个 negative-size array trick or the BOOST_STATIC_ASSERT

这是一个符合 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;

现在是 not-so-fun 部分(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 的迭代器不是问题。

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

对于适用于 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<x>对于除double之外的任何x都没有valid_type,但是我们尝试在[中实例化该类型的实例=11=]。 (void)type_checker 可防止某些编译器针对有效类型发出有关从未使用过的变量的警告。