Стандарт C++11 ввел в язык такую вещь, как пользовательские литералы.
[1] .
А конкретно десяток вариантов определения оператора "" , добавив немного синтаксического сахара, все за исключением одного — шаблонной версии:
Этот вариант отличается тем, что не просто предоставляет альтернативный вариант вызова функции с использованием суффикса, но позволяет определить собственные правила разбора переданного аргумента на этапе компиляции, тем самым расширяя возможности компилятора.template <char.> type operator "" _op();
Например: auto x = 10001000100011001001001010001000_b;
Однако при разработке стандарта было допущено небольшое упущение — шаблонная версия пользовательского литерала позволяет работать только с числовыми аргументами, несмотря на то, что они анализируются посимвольно.
Такое упущение, конечно, не могло остаться незамеченным, и на этапе согласования стандарта С++14 было предложено решение для строковых аргументов.
[2] template <typename CharT, CharT .
String> type operator "" _op();
Вскоре реализовано в компиляторах GCC. [3] и clang (расширение GNU).
Однако стандарт C++14 так и не вошёл в окончательную редакцию.
Однако не будем отчаиваться, есть надежда, что C++17 нас порадует. А пока давайте посмотрим, как мы можем использовать новый тип пользовательских литералов.
Давайте определим шаблон метастроки: template<char .
Chars> struct str { static constexpr const char value[sizeof.(Chars)+1] = {Chars.,'\0'}; static constexpr int size = sizeof.(Chars); }; template<char .
Chars>
constexpr const char str<Chars.>::value[sizeof.(Chars)+1];
Давайте определим наш литерал генератора метастроок: template<typename CharT, CharT .
String>
constexpr str<String.> operator"" _s()
{
return str<String.>();
}
Давайте создадим шаблон структуры данных в виде карты с типами в качестве ключей: template<class Type, class Key>
struct field {
using key = Key;
using type = Type;
type value;
};
template<class,class,int N=0>
struct field_by_type;
template<class Key, class Type, class .
Tail, int N> struct field_by_type<Key, std::tuple<field<Type,Key>,Tail.>, N> { static constexpr int value = N; }; template<class Key, class Head, class .
Tail, int N> struct field_by_type<Key, std::tuple<Head,Tail.>, N> : field_by_type<Key,std::tuple<Tail.>,N+1> {}; template<class .
Fields> struct record { using tuple_type = std::tuple<Fields.>; template<class Key> typename std::tuple_element<field_by_type<Key,tuple_type>::value,tuple_type>::type::type& operator[](Key) { return std::get<field_by_type<Key,tuple_type>::value>(data).
value; } template<class Key> const typename std::tuple_element<field_by_type<Key,tuple_type>::value,tuple_type>::type::type& operator[](Key) const { return std::get<field_by_type<Key,tuple_type>::value>(data).
value;
}
tuple_type data;
};
Поскольку мы собираемся использовать метастроки в качестве типов ключей, давайте добавим немного ввода-вывода: template<class Type, class Key>
std::ostream& operator<< (std::ostream& os, const field<Type,Key> f){
os << Key::value << " = " << f.value << "\n";
return os;
}
template<int I, typename. Ts>
struct print_tuple {
std::ostream& operator() (std::ostream& os, const std::tuple<Ts.>& t) {
os << std::get<sizeof.(Ts)-I>(t);
return print_tuple<I - 1, Ts.>{}(os,t);
}
};
template<typename. Ts>
struct print_tuple<0, Ts.> {
std::ostream& operator() (std::ostream& os, const std::tuple<Ts.>& t) {
return os;
}
};
template<class .
Fields>
std::ostream& operator<< (std::ostream& os, const record<Fields.>& r) {
os << "{\n";
print_tuple<sizeof.(Fields),Fields.>{}(os,r.data);
os << "}";
return os;
}
Ну а теперь сам пример: using Person = record<
field<int, decltype("id"_s)>,
field<std::string, decltype("first_name"_s)>,
field<std::string, decltype("last_name"_s)>
>;
int main(){
Person p;
p["id"_s] = 10;
p["first_name"_s] = "John";
p["last_name"_s] = "Smith";
std::cout << p << "\n";
}
Мы также можем наследовать, добавляя новые функции: class Person
: public record<
field<int, decltype("id"_s)>,
field<std::string, decltype("first_name"_s)>,
field<std::string, decltype("last_name"_s)>
>
{
public: void set_name(const std::string& f,const std::string& l) {
(*this)["first_name"_s] = f;
(*this)["last_name"_s] = l;
};
};
int main(){
Person p;
p["id"_s] = 10;
p.set_name("John","Smith");
std::cout << p << "\n";
}
Объекты результатов статически определяют тип поля на основе заданного ключа.
А при использовании невалидного ключа они не только генерируют ошибку компиляции, но и могут закрасить компилятор clang:
Ссылки
- Пользовательские литералы (cpпредпочтение)
- N3599 Шаблоны литеральных операторов для строк (Ричард Смит)
- [C++1y] Поддержка n3599 — шаблоны литеральных операторов для строк для C++1y. (Проект GCC)
-
Обзор Dell Inspiron-5010-B46F43
19 Oct, 24 -
Каковы Различные Виды Онлайн-Инфекций?
19 Oct, 24