Привет.
На cppcon 2014 был доклад для про современное метопрограммирование шаблонов — слайды.
Там в частности рассказывалось как реализовать проверку существует ли для класса оператор копирующего присваивания. Суть метода в том, чтобы получить тип результата некоторого выражения (в данном случае оператора присваивания) через decltype, и инстанцировать этим типом шаблон. Если выражение корректно (то есть у нас есть оператор присваивания), то результатом параметризации будет один тип (например std::true_type), если нет, то другой тип (например std::false_type).
Пример кода из слайдов:
template< class T >
using copy_assign_t = decltype( declval<T&>( ) = declval< T const& >( ) );
template< class T >
struct is_copy_assignable {
private:
template< class U, class = copy_assign_t<U> >
static true_type try_assign( U&& ); // SFINAE may apply!static false_type try_assign( . . . ); // catch-all overloadpublic:
using type = decltype( try_assign( declval<T>( ) ) );
};
Я попробовал написать общий класс, который возвращает true_type/false_type в зависимости от того, является ли любое переданное выражение корректным. Его можно использовать, чтобы реализовать свои проверки для типов, при этом не требуется писать много кода.
Например нужно проверить, есть ли в классе нестатическая функция get_value():
//Объявляем тип выраженияtemplate <typename T>
using has_get_value_t = decltype(std::declval<T>().get_value());
template <typename T>
struct has_get_value : is_correct_expression_t<T, has_get_value_t> { };
int main() {
std::cout << std::is_same<std::true_type, has_get_value<MyClass>::type>::value << std::endl;
}
Как это реализовано:
template <typename T, template<class> class E>
struct is_correct_expression_t {
private:
template <typename U, typename = E<U>>
static std::true_type try_evaluate(U&&);
static std::false_type try_evaluate(...);
public:
using type = decltype(try_evaluate(std::declval<T>()));
};
Главный вопрос не "как", а "зачем". Собственно, он мучает меня с тех пор, когда я впервые увидел подобные трюки, ЕМНИП, у Джозаттиса. Все, конечно, фокусы, имеют право на жизнь, но — зачем?
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
Здравствуйте, slava_phirsov, Вы писали:
_>Главный вопрос не "как", а "зачем". Собственно, он мучает меня с тех пор, когда я впервые увидел подобные трюки, ЕМНИП, у Джозаттиса. Все, конечно, фокусы, имеют право на жизнь, но — зачем?
Проверка соответствия концепции по синтаксису Зачем? Например для перегрузки, более внятных ошибок компиляции.
Для подобных вещей в язык уже больше десяти лет пытаются добавить концепции, в следующем месяце должны наконец проголосовать
(естественно концепции будут мощнее трюка с decltype — там должно быть автоматическое разбиение на атомы, автоматическая перегрузка, краткий синтаксис).
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>Проверка соответствия концепции по синтаксису Зачем? Например для перегрузки, более внятных ошибок компиляции. EP>Для подобных вещей в язык уже больше десяти лет пытаются добавить концепции, в следующем месяце должны наконец проголосовать
Ну вот концепты, с простым синтаксисом — другой разговор, а вот подобная камасутра, ИМХО, с точки зрения проверки никакой пользы кроме вреда не принесёт.
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
Здравствуйте, slava_phirsov, Вы писали:
_>Ну вот концепты, с простым синтаксисом — другой разговор
Я отвечал на вопрос "зачем?". Простой синтаксис это не "зачем?", а "как?".
_>а вот подобная камасутра, ИМХО, с точки зрения проверки никакой пользы кроме вреда не принесёт.
Всего лишь инструмент, позволяющий делать определённые востребованные вещи
Конечно это как левой ногой правое ухо, но других средств на данный момент нет. И, например, в том же стандарте уже давно поселился std::enable_if.
Здравствуйте, slava_phirsov, Вы писали:
_>Главный вопрос не "как", а "зачем". Собственно, он мучает меня с тех пор, когда я впервые увидел подобные трюки, ЕМНИП, у Джозаттиса. Все, конечно, фокусы, имеют право на жизнь, но — зачем?
Вот прямо сегодня столкнулся с задачей.
Есть класс:
Т.к. он сохраняет ссылку на строку, строка не должна быть временным объектом, но временный объект передать можно т.к. конструктор принимает const std::string&.
Запретить такое поведение можно так:
У нас появляется некоторое требование к интерфейсу класса (нельзя конструировать от временного std::string), который имеет смысл проверять в unit-тестах. Эту проверку как раз можно делать с использованием описанных трюков.
Тест получается значительно сложнее кода, который он проверяет. А так быть не должно. Тест как правило всегда больше проверяемого кода по объему (иногда — на порядок), но он не должен быть сложнее, иначе это не тест, а Не говоря уже о том, что зачастую на тесты сажают людей не самой высокой квалификации. Так тоже быть не должно, кстати, но так бывает, причем нередко. И не стоит этим людям подкладывать такую заподлянку. ИМХО.
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
Здравствуйте, slava_phirsov, Вы писали:
_>Тест получается значительно сложнее кода, который он проверяет. А так быть не должно. Тест как правило всегда больше проверяемого кода по объему (иногда — на порядок), но он не должен быть сложнее, иначе это не тест, а Не говоря уже о том, что зачастую на тесты сажают людей не самой высокой квалификации. Так тоже быть не должно, кстати, но так бывает, причем нередко. И не стоит этим людям подкладывать такую заподлянку. ИМХО.
Так я привёл код реализации. Естественно, чтобы это реализовать нужно знать C++ немного глубже. Но для пользователя код будет выглядеть так: