Этот способ аналогичен приведенному выше решению, но реализован по-разному
.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();
});
Вот один пример ( здесь ):
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 не является типом класса.
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.
Вот еще один пример (последний) 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;
}
int C::*
в вашем ответе? Как C::*
может быть именем параметра?
– Kirill Kobelev
29 January 2016 в 14:14
Библиотека Boost enable_if предлагает хороший чистый интерфейс для использования SFINAE. Один из моих любимых примеров использования - в библиотеке Boost.Iterator . SFINAE используется для включения преобразований типа итератора.
Мне нравится использовать 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) { /* ... */ }
}
На практике я часто нахожу способность проверять условия на полезную способность.
M <= N ? 1 : -1
может работать.
– v.oddou
13 June 2014 в 11:43
int foo[0]
. Я не удивлен, что это поддерживается, поскольку это позволяет очень полезную «конструкцию», заканчивающуюся массивом длиной 0 ». trick ( gcc.gnu.org/onlinedocs/gcc/Zero-Length.html ).
– akim
14 June 2014 в 17:06
error C2466: cannot allocate an array of constant size 0
– v.oddou
16 June 2014 в 02:31
В 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 здесь.
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';
}
...
, а скорееint C::*
, чего я никогда не видел и должен был искать. Нашел ответ для чего это и что он может быть использован здесь: stackoverflow.com/questions/670734/… – HostileFork 12 July 2012 в 07:01