如何只允许具有特定 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
}
请注意,在这种情况下,int
到 double
会很好,但这只是一个示例,在实际代码中,类型必须完全匹配。令我惊讶的是,在这两种情况下,构造函数都没有错误地工作(只有关于未使用的 typedef 的警告)。当在方法内部声明 typedef 时,-1
大小的数组静态断言技巧是否不起作用? 当 IT::value_type
类型错误时如何产生错误?
PS:如果有简单的 C++98 解决方案就好了,但如果这变得太复杂,我也可以接受 C++11 解决方案。
在现代 C++ 中,您可以使用 std::is_same
和 static_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_type
为 double
的任何迭代器,foo()
的实例化都会成功,否则无法编译。
前提是TypeChecker<x>
对于除double
之外的任何x
都没有valid_type
,但是我们尝试在[中实例化该类型的实例=11=]。 (void)type_checker
可防止某些编译器针对有效类型发出有关从未使用过的变量的警告。
我想写一个 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
}
请注意,在这种情况下,int
到 double
会很好,但这只是一个示例,在实际代码中,类型必须完全匹配。令我惊讶的是,在这两种情况下,构造函数都没有错误地工作(只有关于未使用的 typedef 的警告)。当在方法内部声明 typedef 时,-1
大小的数组静态断言技巧是否不起作用? 当 IT::value_type
类型错误时如何产生错误?
PS:如果有简单的 C++98 解决方案就好了,但如果这变得太复杂,我也可以接受 C++11 解决方案。
在现代 C++ 中,您可以使用 std::is_same
和 static_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_type
为 double
的任何迭代器,foo()
的实例化都会成功,否则无法编译。
前提是TypeChecker<x>
对于除double
之外的任何x
都没有valid_type
,但是我们尝试在[中实例化该类型的实例=11=]。 (void)type_checker
可防止某些编译器针对有效类型发出有关从未使用过的变量的警告。