Не могли бы вы объяснить ниже код? Он прекрасно компилируется. Это связано с проверкой того, является ли данный класс базой другого класса [duplicate]

Этот способ аналогичен приведенному выше решению, но реализован по-разному

.social_icon -> некоторый класс с CSS

 <div class="social_icon" id="SOME_ID" data-url="SOME_URL"></div>


 $('.social_icon').click(function(){

        var url = $(this).attr('data-url');
        var win = window.open(url, '_blank');  ///similar to above solution
        win.focus();
   });
93
задан rlbond 11 June 2009 в 19:25
поделиться

7 ответов

Вот один пример ( здесь ):

template<typename T>
class IsClassT {
  private:
    typedef char One;
    typedef struct { char a[2]; } Two;
    template<typename C> static One test(int C::*);
    // Will be chosen if T is anything except a class.
    template<typename C> static Two test(...);
  public:
    enum { Yes = sizeof(IsClassT<T>::test<T>(0)) == 1 };
    enum { No = !Yes };
};

Когда IsClassT<int>::Yes оценивается, 0 не может быть преобразован в int int::*, потому что int не является классом, поэтому он не может иметь указатель на член. Если SFINAE не существует, вы получите ошибку компилятора, что-то вроде «0 не может быть преобразовано в указатель-член для не-класса типа int». Вместо этого он просто использует форму ..., которая возвращает Two и, следовательно, оценивает значение false, int не является типом класса.

55
ответ дан John Kugelman 19 August 2018 в 04:08
поделиться
  • 1
    Что означает ... означает в C ++? – rlbond 12 June 2009 в 01:59
  • 2
    @rlbond, я ответил на ваш вопрос в комментариях к этому вопросу здесь: stackoverflow.com/questions/822059/… . Короче: если обе тестовые функции являются кандидатами и жизнеспособны, тогда «...» имеет худшую стоимость конверсии и, следовательно, никогда не будет приниматься в пользу другой функции. & Quot; ... & Quot; является многоточием, var-arg thing: int printf (char const *, ...); – Johannes Schaub - litb 13 June 2009 в 00:25
  • 3
    Ссылка была изменена на blog.olivierlanglois.net/index.php/2007/09/01/… – tstenner 25 August 2009 в 18:32
  • 4
    Более странная вещь здесь IMO - это не ..., а скорее int C::*, чего я никогда не видел и должен был искать. Нашел ответ для чего это и что он может быть использован здесь: stackoverflow.com/questions/670734/… – HostileFork 12 July 2012 в 07:01

C ++ 17, вероятно, предоставит общие средства для запросов к функциям. Подробнее см. В N4502 , но в качестве самостоятельного примера рассмотрим следующее.

Эта часть является постоянной частью, помещает ее в заголовок.

// See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4502.pdf.
template <typename...>
using void_t = void;

// Primary template handles all types not supporting the operation.
template <typename, template <typename> class, typename = void_t<>>
struct detect : std::false_type {};

// Specialization recognizes/validates only types supporting the archetype.
template <typename T, template <typename> class Op>
struct detect<T, Op, void_t<Op<T>>> : std::true_type {};

В следующем примере, взятом из N4502 , показано использование:

// Archetypal expression for assignment operation.
template <typename T>
using assign_t = decltype(std::declval<T&>() = std::declval<T const &>())

// Trait corresponding to that archetype.
template <typename T>
using is_assignable = detect<T, assign_t>;

. По сравнению с другими реализациями это довольно просто: приведенный набор инструментов ( void_t и detect). Кроме того, сообщалось (см. N4502 ), что оно заметно более эффективно (время компиляции и потребления памяти компилятора), чем предыдущие.

Вот живой пример , который включает настройки переносимости для GCC pre 5.1.

1
ответ дан akim 19 August 2018 в 04:08
поделиться

Вот еще один пример (последний) SFINAE , основанный на ответ Грега Роджерса :

template<typename T>
class IsClassT {
    template<typename C> static bool test(int C::*) {return true;}
    template<typename C> static bool test(...) {return false;}
public:
    static bool value;
};

template<typename T>
bool IsClassT<T>::value=IsClassT<T>::test<T>(0);

Таким образом, вы можете проверить значение value, чтобы узнать, является ли T классом или нет:

int main(void) {
    std::cout << IsClassT<std::string>::value << std::endl; // true
    std::cout << IsClassT<int>::value << std::endl;         // false
    return 0;
}
2
ответ дан Community 19 August 2018 в 04:08
поделиться

Библиотека Boost enable_if предлагает хороший чистый интерфейс для использования SFINAE. Один из моих любимых примеров использования - в библиотеке Boost.Iterator . SFINAE используется для включения преобразований типа итератора.

7
ответ дан David Joyner 19 August 2018 в 04:08
поделиться

Мне нравится использовать SFINAE для проверки булевых условий.

template<int I> void div(char(*)[I % 2 == 0] = 0) {
    /* this is taken when I is even */
}

template<int I> void div(char(*)[I % 2 == 1] = 0) {
    /* this is taken when I is odd */
}

Это может быть весьма полезно. Например, я использовал его для проверки того, что список инициализаторов, собранный с использованием служебной запятой, не больше фиксированного размера

template<int N>
struct Vector {
    template<int M> 
    Vector(MyInitList<M> const& i, char(*)[M <= N] = 0) { /* ... */ }
}

Список принимается только в том случае, когда M меньше N, а это означает, что инициализатор списка не слишком много элементов.

Синтаксис char(*)[C] означает: Указатель на массив с типом элемента char и размером C. Если C false (0 здесь), мы получаем недопустимый тип char(*)[0], указатель на массив нулевого размера: SFINAE делает его таким, чтобы затем шаблон игнорировался.

Выражено с boost::enable_if, это выглядит так

template<int N>
struct Vector {
    template<int M> 
    Vector(MyInitList<M> const& i, 
           typename enable_if_c<(M <= N)>::type* = 0) { /* ... */ }
}

На практике я часто нахожу способность проверять условия на полезную способность.

81
ответ дан Johannes Schaub - litb 19 August 2018 в 04:08
поделиться
  • 1
    @Johannes Достаточно странно, что GCC (4.8) и Clang (3.2) принимают объявление массивов размера 0 (так что тип не действительно «недействителен»), но он ведет себя корректно в вашем коде. Вероятно, специальная поддержка для этого случая в случае SFINAE vs. "regular" использования типов. – akim 5 February 2013 в 11:07
  • 2
    @akim: если это правда (странно?! с каких пор?), то, возможно, M <= N ? 1 : -1 может работать. – v.oddou 13 June 2014 в 11:43
  • 3
    @ v.oddou Просто попробуйте int foo[0]. Я не удивлен, что это поддерживается, поскольку это позволяет очень полезную «конструкцию», заканчивающуюся массивом длиной 0 ». trick ( gcc.gnu.org/onlinedocs/gcc/Zero-Length.html ). – akim 14 June 2014 в 17:06
  • 4
    @akim: да, что я думал - & gt; C99. Это не разрешено в C ++, вот что вы получаете с современным компилятором: error C2466: cannot allocate an array of constant size 0 – v.oddou 16 June 2014 в 02:31
  • 5
    @ v.oddou Нет, я действительно имел в виду C ++, а на самом деле C ++ 11: и clang ++, и g ++ принимают его, и я указал на страницу, которая объясняет, почему это полезно. – akim 16 June 2014 в 07:45

В C ++ 11 тесты SFINAE стали намного красивее. Вот несколько примеров общих применений:

Выберите перегрузку функции в зависимости от признаков

template<typename T>
std::enable_if_t<std::is_integral<T>::value> f(T t){
    //integral version
}
template<typename T>
std::enable_if_t<std::is_floating_point<T>::value> f(T t){
    //floating point version
}

Используя так называемую икону sink типа, вы можете делать довольно произвольные тесты типа проверяя, имеет ли он член, и если этот элемент имеет определенный тип

//this goes in some header so you can use it everywhere
template<typename T>
struct TypeSink{
    using Type = void;
};
template<typename T>
using TypeSinkT = typename TypeSink<T>::Type;

//use case
template<typename T, typename=void>
struct HasBarOfTypeInt : std::false_type{};
template<typename T>
struct HasBarOfTypeInt<T, TypeSinkT<decltype(std::declval<T&>().*(&T::bar))>> :
    std::is_same<typename std::decay<decltype(std::declval<T&>().*(&T::bar))>::type,int>{};


struct S{
   int bar;
};
struct K{

};

template<typename T, typename = TypeSinkT<decltype(&T::bar)>>
void print(T){
    std::cout << "has bar" << std::endl;
}
void print(...){
    std::cout << "no bar" << std::endl;
}

int main(){
    print(S{});
    print(K{});
    std::cout << "bar is int: " << HasBarOfTypeInt<S>::value << std::endl;
}

Вот живой пример: http://ideone.com/dHhyHE Я также недавно написал весь раздел о SFINAE и отправке тегов в моем блоге (бесстыдный плагин, но релевантный) http://metaporky.blogspot.de/2014/08/part-7-static-dispatch-function.html

Обратите внимание, что с C ++ 14 есть std :: void_t, который по существу совпадает с моим TypeSink здесь.

6
ответ дан odinthenerd 19 August 2018 в 04:08
поделиться
  • 1
    Ваш первый блок кода переопределяет один и тот же шаблон. – T.C. 21 September 2014 в 06:43
  • 2
    Поскольку нет типа, для которого is_integral и is_floating_point оба являются истинными, это должно быть либо либо, либо потому, что SFINAE удалит хотя бы один. – odinthenerd 7 October 2014 в 18:53
  • 3
    Вы переопределяете один и тот же шаблон с разными аргументами шаблона по умолчанию. Вы пытались скомпилировать его? – T.C. 7 October 2014 в 18:56
  • 4
    Ах, как глупо со мной, спасибо, что указали! Просто исправил это. – odinthenerd 7 October 2014 в 19:14
  • 5
    Я новичок в метапрограммировании шаблонов, поэтому хотел понять этот пример. Есть ли причина, по которой вы используете TypeSinkT<decltype(std::declval<T&>().*(&T::bar))> в одном месте, а затем TypeSinkT<decltype(&T::bar)> в другом? Также необходимо & в std::declval<T&>? – Kevin Doyon 22 December 2015 в 22:44

Вот одна хорошая статья SFINAE: Введение в концепцию SFINAE C ++: интроспекция времени компиляции члена класса .

Обозначьте это следующим образом:

/*
 The compiler will try this overload since it's less generic than the variadic.
 T will be replace by int which gives us void f(const int& t, int::iterator* b = nullptr);
 int doesn't have an iterator sub-type, but the compiler doesn't throw a bunch of errors.
 It simply tries the next overload. 
*/
template <typename T> void f(const T& t, typename T::iterator* it = nullptr) { }

// The sink-hole.
void f(...) { }

f(1); // Calls void f(...) { }

template<bool B, class T = void> // Default template version.
struct enable_if {}; // This struct doesn't define "type" and the substitution will fail if you try to access it.

template<class T> // A specialisation used if the expression is true. 
struct enable_if<true, T> { typedef T type; }; // This struct do have a "type" and won't fail on access.

template <class T> typename enable_if<hasSerialize<T>::value, std::string>::type serialize(const T& obj)
{
    return obj.serialize();
}

template <class T> typename enable_if<!hasSerialize<T>::value, std::string>::type serialize(const T& obj)
{
    return to_string(obj);
}

declval - это утилита, которая дает вам «поддельную ссылку» на объект типа, который не может быть легко сконструирован. declval действительно удобен для наших конструкций SFINAE.

struct Default {
    int foo() const {return 1;}
};

struct NonDefault {
    NonDefault(const NonDefault&) {}
    int foo() const {return 1;}
};

int main()
{
    decltype(Default().foo()) n1 = 1; // int n1
//  decltype(NonDefault().foo()) n2 = n1; // error: no default constructor
    decltype(std::declval<NonDefault>().foo()) n2 = n1; // int n2
    std::cout << "n2 = " << n2 << '\n';
}
0
ответ дан zangw 19 August 2018 в 04:08
поделиться
Другие вопросы по тегам:

Похожие вопросы: