Что должно произойти, так это то, что «именованные элементы» добавляются как кажущиеся свойства объекта document
. Это действительно плохая идея, так как позволяет именам элементов сталкиваться с реальными свойствами document
.
IE затруднил ситуацию, добавив именованные элементы в качестве свойств объекта window
. Это вдвойне плохо, потому что теперь вам нужно избегать именования ваших элементов после того, как захочет использовать какой-либо из объектов document
или window
, которые вы (или любой другой библиотечный код в своем проекте) захотите использовать.
Это также означает, что эти элементы видны как глобальные переменные. К счастью, в этом случае любые реальные глобальные объявления var
или function
в вашем коду затушевывают их, поэтому вам не нужно так беспокоиться об именах здесь, но если вы попытаетесь выполнить присвоение глобальной переменной с конфликтом имя и вы забудете объявить его var
, вы получите сообщение об ошибке в IE, поскольку оно пытается присвоить значение самому элементу.
Обычно считается, что неправильная практика пропускает var
, так как а также полагаться на названные элементы, которые видны на window
или в виде глобальных. Придерживайтесь document.getElementById
, который более широко поддерживается и менее неоднозначен. Вы можете написать тривиальную функцию обертки с более коротким именем, если вам не нравится ввод текста. В любом случае, нет смысла использовать кеш-поиск с id-элементом, поскольку браузеры обычно оптимизируют вызов getElementById
, чтобы использовать быстрый поиск; все, что вы получаете, это проблемы, когда элементы меняются id
или добавляются / удаляются из документа.
Opera скопировала IE, затем присоединился WebKit, и теперь обе ранее нестандартная практика размещения именованных элементов в document
, и ранее применявшаяся только для IE практика помещать их в window
, является стандартизированной HTML5, подход которой заключается в том, чтобы документировать и стандартизировать каждую ужасную практику, причиненную на нас авторами браузеров, делая их частью сети навсегда. Таким образом, Firefox 4 также будет поддерживать это.
Что такое «названные элементы»? Все, что имеет id
, и все, что используется name
для целей идентификации, то есть формы, изображения, привязки и несколько других, но не другие несвязанные экземпляры атрибута name
, например, имена в полях ввода формы, имена параметров в или тип метаданных в
. «Идентификация»
name
s - это те, которые следует избегать в пользу id
.
Я думаю, что можно специализировать характеристики для лямбда-выражений и выполнять сопоставление с образцом по сигнатуре безымянного функтора. Вот код, который работает на g ++ 4.5. Хотя это работает, сопоставление с образцом в лямбда-выражении, похоже, работает вопреки интуиции. У меня есть встроенные комментарии.
struct X
{
float operator () (float i) { return i*2; }
// If the following is enabled, program fails to compile
// mostly because of ambiguity reasons.
//double operator () (float i, double d) { return d*f; }
};
template <typename T>
struct function_traits // matches when T=X or T=lambda
// As expected, lambda creates a "unique, unnamed, non-union class type"
// so it matches here
{
// Here is what you are looking for. The type of the member operator()
// of the lambda is taken and mapped again on function_traits.
typedef typename function_traits<decltype(&T::operator())>::return_type return_type;
};
// matches for X::operator() but not of lambda::operator()
template <typename R, typename C, typename... A>
struct function_traits<R (C::*)(A...)>
{
typedef R return_type;
};
// I initially thought the above defined member function specialization of
// the trait will match lambdas::operator() because a lambda is a functor.
// It does not, however. Instead, it matches the one below.
// I wonder why? implementation defined?
template <typename R, typename... A>
struct function_traits<R (*)(A...)> // matches for lambda::operator()
{
typedef R return_type;
};
template <typename F>
typename function_traits<F>::return_type
foo(F f)
{
return f(10);
}
template <typename F>
typename function_traits<F>::return_type
bar(F f)
{
return f(5.0f, 100, 0.34);
}
int f(int x) { return x + x; }
int main(void)
{
foo(f);
foo(X());
bar([](float f, int l, double d){ return f+l+d; });
}
Трюк void_t
может помочь. Как работает `void_t` ?
Если у вас нет C ++ 17, вам нужно будет включить определение void_t
:
template<typename... Ts> struct make_void { typedef void type;};
template<typename... Ts> using void_t = typename make_void<Ts...>::type;
Добавить дополнительный аргумент шаблона к исходному шаблону, по умолчанию равный void
:
template <typename T, typename = void>
struct function_traits;
Объект признаков для простых функций такой же, как у вас уже есть:
template <typename R, typename... A>
struct function_traits<R (*)(A...)>
{
using return_type = R;
using class_type = void;
using args_type = std:: tuple< A... >;
};
Для не методы const:
template <typename R, typename... A>
struct function_traits<R (*)(A...)>
{
using return_type = R;
using class_type = void;
using args_type = std:: tuple< A... >;
};
Не забывайте const
методы:
template <typename R, typename C, typename... A>
struct function_traits<R (C::*)(A...) const> // const
{
using return_type = R;
using class_type = C;
using args_type = std:: tuple< A... >;
};
Наконец, важная черта. Для данного типа класса, включая лямбда-типы, мы хотим перейти от T
к decltype(&T::operator())
. Мы хотим убедиться, что эта черта доступна только для типов T
, для которых ::operator()
доступна, и это то, что void_t
делает для нас. Чтобы применить это ограничение, нам нужно поместить &T::operator()
в сигнатуру признака где-то, поэтому template <typename T> struct function_traits<T, void_t< decltype(&T::operator())
template <typename T>
struct function_traits<T, void_t< decltype(&T::operator()) > >
: public function_traits< decltype(&T::operator()) >
{
};
Метод operator () в (не mutable
, неуниверсальных) лямбдах const
, что объясняет, почему нам нужен шаблон const
выше.
operator()
. Если вы пересмотрите свой дизайн, вы найдете другой, более гибкий подход.