C++ 检查变量类型是否为iterable?
是否有任何方法可以检查任意变量类型是否为iterable 那么,为了检查它是否有索引元素,或者我可以实际循环它的子元素?(例如使用foreach?) 是否可以为此创建通用模板C++ 检查变量类型是否为iterable?,c++,templates,typetraits,iterable,c++03,C++,Templates,Typetraits,Iterable,C++03,是否有任何方法可以检查任意变量类型是否为iterable 那么,为了检查它是否有索引元素,或者我可以实际循环它的子元素?(例如使用foreach?) 是否可以为此创建通用模板 我在搜索其他编程语言时发现了一些技术。但是,仍然需要在C++中找到如何做到这一点。 < P>这取决于你所说的“迭代”。它是C++中一个松散的概念,因为您可以用许多不同的方式实现迭代器。 如果byforeach指的是C++11基于范围的for循环,则类型需要定义begin()和end()方法,并返回响应运算符的迭代器=,,
我在搜索其他编程语言时发现了一些技术。但是,仍然需要在C++中找到如何做到这一点。 < P>这取决于你所说的“迭代”。它是C++中一个松散的概念,因为您可以用许多不同的方式实现迭代器。 如果by
foreach
指的是C++11基于范围的for循环,则类型需要定义begin()
和end()
方法,并返回响应运算符的迭代器=代码>,,
operator++
和operator*
如果您是指Boost的Boost_FOREACH helper,请参见
如果在您的设计中有一个所有iterable容器都继承自的通用接口,那么您可以使用C++11:
结构A:IterableInterface{}
结构B{}
模板
constexpr bool是不可编辑的(){
return std::是::value的_base_;
}
是_iterable();//真的
是_iterable();//假的
是使用此兼容的
输出
注意:C++11(和C++14)提供了许多关于可移植性的信息,但没有提供任何信息
另请参见和的类似答案
这个答案是公开的-你可以为此创造一个特质:
namespace detail
{
// To allow ADL with custom begin/end
using std::begin;
using std::end;
template <typename T>
auto is_iterable_impl(int)
-> decltype (
begin(std::declval<T&>()) != end(std::declval<T&>()), // begin/end and operator !=
void(), // Handle evil operator ,
++std::declval<decltype(begin(std::declval<T&>()))&>(), // operator ++
void(*begin(std::declval<T&>())), // operator*
std::true_type{});
template <typename T>
std::false_type is_iterable_impl(...);
}
template <typename T>
using is_iterable = decltype(detail::is_iterable_impl<T>(0));
名称空间详细信息
{
//允许具有自定义开始/结束的ADL
使用std::begin;
使用std::end;
模板
自动可执行(int)
->脱模(
begin(std::declval())!=end(std::declval()),//开始/结束和运算符=
void(),//句柄邪恶运算符,
++std::declval(),//运算符++
void(*开始(std::declval()),//运算符*
std::真_类型{});
模板
std::false_类型为_iterable_impl(…);
}
模板
使用is_iterable=decltype(细节::is_iterable_impl(0));
.。它正在使用,下面是该示例的一个稍加修改的版本(以防该链接的内容随时间而更改):
话虽如此,它检查的只是begin()const
和end()const
的声明,因此,即使是以下内容也被验证为iterable:
struct Container
{
void begin() const;
void end() const;
};
std::cout << is_iterable_v<Container> << '\n'; // prints true
struct容器
{
void begin()常量;
void end()常量;
};
std::cout或者,如果(像我一样)您讨厌每个SFINAE解决方案都是一大块虚拟结构定义,其中包含::type
和::value
无意义的内容,下面是一个使用快速且(非常)脏的一行程序的示例:
template <
class Container,
typename ValueType = decltype(*std::begin(std::declval<Container>()))>
static void foo(Container& container)
{
for (ValueType& item : container)
{
...
}
}
模板<
类容器,
typename ValueType=decltype(*std::begin(std::declval())>
静态void foo(容器和容器)
{
对于(值类型和项目:容器)
{
...
}
}
最后一个模板参数在一个步骤中执行多项操作:
检查类型是否具有begin()
成员函数或等效函数
检查begin()
函数是否返回定义了operator*()
的内容(迭代器的典型情况)
确定从反引用迭代器中得到的类型,并将其保存,以备在模板实现中使用
限制:不重复检查是否有匹配的end()
成员函数
如果您想要更健壮/彻底/可重用的解决方案,请选择其他优秀的建议解决方案。如果您在C++11及更高版本的保护伞下,当您必须专门处理一个属性时,SFINAE检查的一种常用方法是:
template<class T, class = decltype(<expression that must compile>)>
inline constexpr bool expression_works(int) { return true; }
template<class>
inline constexpr bool expression_works(unsigned) { return false; }
template<class T, bool = expression_works<T>(42)>
class my_class;
template<class T>
struct my_class<T, true>
{ /* Implementation when true */ };
template<class T>
struct my_class<T, false>
{ /* Implementation when false */ };
如果您需要更多的通用性,一种表示某种东西是可移植的方法还可以包括用户定义的类型,这些类型为begin
和end
带来自己的重载,因此您需要在此处应用一些adl
:
namespace _adl_begin {
using std::begin;
template<class T>
inline auto check() -> decltype(begin(std::declval<T>())) {}
}
template<class T, class = decltype(_adl_begin::check<T>())>
inline constexpr bool is_iterable(int) { return true; }
template<class>
inline constexpr bool is_iterable(unsigned) { return false; }
namespace\u adl\u begin{
使用std::begin;
模板
内联自动检查()->decltype(begin(std::declval()){}
}
模板
内联constexpr bool是_iterable(int){return true;}
模板
内联constexpr bool是不可数(无符号){return false;}
您可以使用此技术来实现更适合您实际环境的解决方案。假设iterability被表示为一种类型,可能是RTTI?你能提供一两个例子吗?我正在使用Maya API及其类。您可以迭代其MVectorArray、MIntArray类,也可以迭代MVector、MPoint、MColor类,因为它们包含多个数值(或MString/MStringArray的字符串)。现在,我正在尝试创建一个模板,它支持将任意值相加(或相乘、相除和更通用的数学公式)。这意味着iterable元素要在其上迭代,并向其每个元素/索引添加一个元素。这有什么意义吗?这是一个很好的例子,它让我很好地使用了std::declval
。非常感谢你!这是函数的SFINE
,使用上面的decltype
是解决许多不同问题的最优雅的方法(为什么我们需要任何其他方法?使用此方法和enable_if
)。我只看到了一个侧重点,那就是需要填充void()
来打破重载运算符,
。这个答案不需要它,但通常在某些情况下可能是必要的。@Jarod42,为什么在声明auto是可执行的(int)
中有一个int
?另外,这里的邪恶操作符是什么?@user2635088:int
用于在两种重载都可行时提供命令f(int)
比f(…)
更匹配。谢谢您的回答!如果没有它,我将永远无法创作。此外,你的作品
template <typename T, typename = void>
struct is_iterable : std::false_type {};
// this gets used only when we can call std::begin() and std::end() on that type
template <typename T>
struct is_iterable<T, std::void_t<decltype(std::begin(std::declval<T>())),
decltype(std::end(std::declval<T>()))
>
> : std::true_type {};
// Here is a helper:
template <typename T>
constexpr bool is_iterable_v = is_iterable<T>::value;
std::cout << std::boolalpha;
std::cout << is_iterable_v<std::vector<double>> << '\n';
std::cout << is_iterable_v<std::map<int, double>> << '\n';
std::cout << is_iterable_v<double> << '\n';
struct A;
std::cout << is_iterable_v<A> << '\n';
true
true
false
false
struct Container
{
void begin() const;
void end() const;
};
std::cout << is_iterable_v<Container> << '\n'; // prints true
template <
class Container,
typename ValueType = decltype(*std::begin(std::declval<Container>()))>
static void foo(Container& container)
{
for (ValueType& item : container)
{
...
}
}
template<class T, class = decltype(<expression that must compile>)>
inline constexpr bool expression_works(int) { return true; }
template<class>
inline constexpr bool expression_works(unsigned) { return false; }
template<class T, bool = expression_works<T>(42)>
class my_class;
template<class T>
struct my_class<T, true>
{ /* Implementation when true */ };
template<class T>
struct my_class<T, false>
{ /* Implementation when false */ };
template<class T, class = decltype(std::begin(std::declval<T>()))
inline constexpr bool is_iterable(int) { return true; }
template<class>
inline constexpr bool is_iterable(unsigned) { return false; }
namespace _adl_begin {
using std::begin;
template<class T>
inline auto check() -> decltype(begin(std::declval<T>())) {}
}
template<class T, class = decltype(_adl_begin::check<T>())>
inline constexpr bool is_iterable(int) { return true; }
template<class>
inline constexpr bool is_iterable(unsigned) { return false; }