簡體   English   中英

創建“is_iterable”特征時的 SFINAE 問題 - 這是 gcc 錯誤嗎?

[英]SFINAE issue in creating an “is_iterable” trait - is this a gcc bug?

下面的代碼嘗試(不使用 c++11)創建一個特征來識別一個類型是否以 STL 方式可迭代:

#include <iostream>
#include <vector>

template<typename C>
struct IsIterable
{
    typedef char true_type; 
    typedef long false_type; 

    template<class T> static true_type  is_beg_iterable(
        typename T::const_iterator = C().begin()); 
    template<class T> static false_type is_beg_iterable(...); 

    enum { value = sizeof(is_beg_iterable<C>()) == sizeof(true_type) }; 
};

int main() {
    std::cout << IsIterable<std::vector<int>>::value << std::endl;
}

還有一個is_end_iterable方法,為簡潔起見在此省略

該代碼gcc 4.9.2 *(以及舊版本)和 clang 而失敗並在 VS2012 中成功 我的斷言是可變參數版本在重載解析中總是排在最后(因此應該沒有歧義),那么誰在這里?

是否有跨平台的解決方法/替代方案?

我現在看到較新版本的 VS 也拒絕代碼,所以最后一個問題變得更重要了

從 C++17 開始,定義is_iterable特征的慣用方法是:

#include <type_traits>
#include <iterator>

namespace is_iterable_impl
{
    using std::begin, std::end;

    template<class T>
    using check_specs = std::void_t<
        std::enable_if_t<std::is_same_v<
            decltype(begin(std::declval<T&>())), // has begin()
            decltype(end(std::declval<T&>()))    // has end()
        >>,                                      // ... begin() and end() are the same type ...
        decltype(*begin(std::declval<T&>()))     // ... which can be dereferenced
    >;

    template<class T, class = void>
    struct is_iterable
    : std::false_type
    {};

    template<class T>
    struct is_iterable<T, check_specs<T>>
    : std::true_type
    {};
}

template<class T>
using is_iterable = is_iterable_impl::is_iterable<T>;

template<class T>
constexpr bool is_iterable_v = is_iterable<T>::value;

現場演示


我們是std::declval<T&>()所以我們的特征適用於數組。 如您所見, std::beginstd::end有一個數組重載,它需要一個引用。

這只適用於函數的實際參數比省略號轉換更好的匹配。 請記住,沒有參數的具有默認值的參數不參與重載決議。

解決方案是添加另一個參數並向其傳遞一個參數:

template<class T> static true_type  is_beg_iterable(int,   // <- for disambiguation
    typename T::const_iterator = C().begin());

template<class T> static false_type is_beg_iterable(...); 

enum { value = sizeof(is_beg_iterable<C>(0)) == sizeof(true_type) }; 
  //                                     ^

活生生的例子。

以下作品(至少使用 gcc 4.9.2):

template<typename C>
struct IsIterable
{
    typedef char true_type;
    typedef long false_type;

    template<class T> static true_type  is_beg_iterable(int,
        typename T::const_iterator = C().begin());
    template<class T> static false_type is_beg_iterable(...);

    enum { value = sizeof(is_beg_iterable<C>(0)) == sizeof(true_type) };
};

活生生的例子

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM