[英]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::begin
和std::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.