C++ 检查变量类型是否为iterable?

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()方法,并返回响应运算符的迭代器=,,

是否有任何方法可以检查任意变量类型是否为iterable

那么,为了检查它是否有索引元素,或者我可以实际循环它的子元素?(例如使用foreach?)

是否可以为此创建通用模板


我在搜索其他编程语言时发现了一些技术。但是,仍然需要在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; }