Делайте , а не , используйте enable_if
для выполнения ваших требований.Использование enable_if
сделает функцию «исчезнувшей», что может сбить пользователя с толку.Типичным симптомом является сообщение об ошибке, например error: no matching function for call to <i>expression</i>
.Это точно не говорит пользователю о том, что требование нарушено.
Вместо этого вам следует применять свои требования, используя static_assert
, предполагая, что C ++ 0x.Если вы используете C ++ 03, то следует ли использовать эмуляцию static_assert
(например, Boost's STATIC_ASSERT
) или нет, так как это обычно означает обмен одного сообщения об ошибке на другое.
Контрастность:
// SFINAE for types that do not decay to int
template<
typename T
, typename = typename std::enable_if<
std::is_same<
typename std::decay<T>::type
, int
>::value
>::type
>
void
f(T&&)
{}
// using static assert instead
template<
typename T
>
void
g(T&&)
{
static_assert( std::is_same<typename std::decay<T>::type, int>::value
, "Constraints violation" );
}
При использовании GCC я получаю следующую ошибку при выполнении f("violation")
(оба сообщения содержат имя файла и номер строки):
error: no matching function for call to 'f(const char [10])'
С другой стороны, g("violation")
выход:
error: static assertion failed: "Constraints violation"
Теперь представьте, что вы используете четкие, явные сообщения в своих утверждениях, такие как foo: parameter type must be CopyConstructible
внутри шаблона foo
.
С учетом сказанного, SFINAE иstatic_assert
несколько антагонистичны, поэтому наличие явных сообщений о нарушении ограничений и умных перегрузок не всегда возможно и / или просто.
То, что вы хотите сделать, легко достигается с помощью Boost.ConceptCheck .Однако для этого требуется написать внеплановый код: класс ограничений.Я также не думаю, что он использует static_assert
там, где это возможно, поэтому сообщения об ошибках могут быть не такими хорошими.Это может быть изменено в будущем.
Другая возможность - использовать черты типа static_assert
+.Что интересно в этом подходе, так это то, что в C ++ 0x библиотека содержит множество полезных особенностей, которые вы можете использовать прямо из коробки без написания внепланового кода.Еще более интересным является то, что использование признаков не ограничивается ограничениями записи, их также можно использовать с SFINAE для создания умных перегрузок.
Однако нет никакой черты, доступной для проверки,Тип поддерживает определенный член операции, возможно, из-за способа, которым C ++ обрабатывает имена функций.Мы также не можем использовать что-то вроде has_member<T, &T::member_to_test_for>
, потому что это имело бы смысл только в том случае, если бы элемент, для которого мы тестировали, существовал в первую очередь (не считая таких вещей, как перегрузки и тот факт, что нам также необходимо передать подпись членаtrait).
Вот как можно преобразовать произвольное выражение в trait:
template<typename T>
struct void_ {
typedef void type;
};
template<typename T>
struct trait {
private:
typedef char yes[1];
typedef char no[2];
template<typename U>
static
yes&
test(U&&
, typename void_<decltype( std::declval<U&>().member() )>::type* = 0);
static
no&
test(...);
public:
static constexpr bool value = sizeof test(std::declval<T>()) == sizeof(yes);
};
Обратите внимание, насколько это существенно.Написание класса ограничений Boost.ConceptCheck может быть проще (но помните, что его нельзя использовать для SFINAE).
Произвольное выражение - std::declval<U&>().member()
.Здесь требования таковы, что при заданной lvalue ссылке U
(или T
для случая, когда признак истинен, если хотите), тогда вызов member()
для него действителен.
Youтакже может проверить, что тип этого выражения (т. е. тип результата какой-либо перегрузки member
был выбран для этого выражения) можно преобразовать в тип (не проверять, является ли этим типом;слишком строгие без уважительной причины).Это, однако, раздуло бы эту черту, снова сделав это в пользу класса ограничений.
Я не знаю способа сделать static_assert
частью сигнатуры шаблона функции (кажется, это что-товы хотите), но он может появиться внутри шаблона класса.Boost.ConceptCheck также не поддерживает это.