Вас может смутить "hello"
; это литерал, но (в некотором смысле) также lvalue (если константа). "hello"
создает объект, который не исчезает после окончания строки, массив константных символов, состоящий из {'h', 'e', 'l', 'l', 'o', '\0'}
. Два разных "hello"
могут относиться к одному и тому же объекту или нет.
6
не делает то же самое; в программе на C++ нет постоянной 6
с константой 6
в ней, есть постоянная "hello"
в программе на C++ со строковой константой "hello"
в ней.
Литерал 6
может привести к созданию экземпляра временного объекта. Время жизни этого временного объекта - до конца выражения, в котором оно находится ("конец строки", где оно находится).
Вы не можете отличить временное, созданное 6
, от временного, возвращенного функцией во время вызова функции. Это к счастью, потому что оба являются временными с одинаковыми преимуществами и недостатками.
Указатель на этот временный объект будет недействительным в этот момент; даже ==
или <
для этого указателя является неопределенным поведением.
Итак, a.cache(6)
— плохой план.
Теперь мы можем сделать что-то ужасное.
unsigned long long const& operator""_lvalue(unsigned long long x) {
thread_local unsigned long long value;
value = x;
return value;
}
живой пример.
Это создает статическое значение длительности хранения lvalue value
и копирует в него x
. Таким образом, 6_lvalue + 4_lvalue
будет либо 8, либо 12, а не 10, так как единственное lvalue будет перезаписано.
Мы можем решить эту проблему перезаписи, увеличив злоупотребление шаблонами.
template<int P>
constexpr unsigned long long pow_( unsigned x, std::size_t tens ) {
if (tens == 0) return x;
return P*pow_<P>(x, tens-1);
}
template<int base>
constexpr unsigned long long ucalc(std::integer_sequence<char>) {
return 0;
}
constexpr unsigned digit( char c ) {
if (c >= '0' && c <= '9') return c-'0';
if (c >= 'a' && c <= 'z') return c-'a'+10;
if (c >= 'A' && c <= 'Z') return c-'A'+10;
exit(-1);
}
template<int base, char c0, char...chars>
constexpr unsigned long long ucalc(std::integer_sequence<char, c0, chars...>) {
return pow_<base>( digit(c0), sizeof...(chars) ) + ucalc<base>( std::integer_sequence<char, chars...>{} );
}
template <char... chars>
constexpr unsigned long long calc(std::integer_sequence<char, chars...>) {
return ucalc<10>(std::integer_sequence<char, chars...>{});
}
template <char... chars>
constexpr unsigned long long calc(std::integer_sequence<char, '0', 'x', chars...>) {
return ucalc<16>(std::integer_sequence<char, chars...>{});
}
template <char... chars>
constexpr unsigned long long calc(std::integer_sequence<char, '0', 'X', chars...>) {
return ucalc<16>(std::integer_sequence<char, chars...>{});
}
template <char... chars>
constexpr unsigned long long calc(std::integer_sequence<char, '0', 'b', chars...>) {
return ucalc<2>(std::integer_sequence<char, chars...>{});
}
template <char... chars>
constexpr unsigned long long calc(std::integer_sequence<char, '0', 'B', chars...>) {
return ucalc<2>(std::integer_sequence<char, chars...>{});
}
template <char... chars>
constexpr unsigned long long calc(std::integer_sequence<char, '0', chars...>) {
return ucalc<8>(std::integer_sequence<char, chars...>{});
}
template <char... chars>
constexpr unsigned long long calc() {
return calc( std::integer_sequence<char, chars...>{} );
}
template<class T, T x>
constexpr T lvalue = x;
template <char... chars>
unsigned long long const& operator "" _lvalue() {
return lvalue<unsigned long long, calc<chars...>()>;
}
живой пример. Примерно половина из них приходится на поддержку 0b
, 0x
и 0
двоичных/шестнадцатеричных/восьмеричных форматов.
Использовать:
a.cache(6_lvalue);
В качестве альтернативы в С++ 17 вы можете сделать это:
template<auto x>
constexpr auto lvalue = x;
or in C++14
template<class T, T x>
constexpr T lvalue = x;
с
#define LVALUE(...) lvalue<std::decay_t<decltype(__VA_ARGS__)>, __VA_ARGS__>
В С++ 17 это lvalue<7>
, а в С++ 14 — LVALUE(7)
.
person
Yakk - Adam Nevraumont
schedule
01.05.2017
template <typename T, T N> constexpr T cliteral = N;
, использование:a.cache(cliteral<int, 6>)
. В C++17:template <auto N> inline constexpr auto cliteral = N;
, использование:a.cache(cliteral<6>)
. - person Kerrek SB   schedule 01.05.2017MyClass
создаются с помощью макроса как локальные и живут только до тех пор, пока не покинут свою область действия, поэтому кэширование других локальных объектов, созданных до них, нормально. - person onqtam   schedule 01.05.2017L
, ничего не даст? - person onqtam   schedule 01.05.2017L
, создающего lvalue, — самая веселая вещь, которую я слышал за всю неделю :-) - person Kerrek SB   schedule 01.05.2017#define LITERAL_TO_LVALUE(x) cliteral<decltype(x), x>
. Вы можете оставить свой комментарий в качестве ответа, и я приму его - person onqtam   schedule 01.05.2017a.cache(6);
? - person aschepler   schedule 01.05.20176
не является допустимым после окончания оператора, в котором находится6
. Поэтому кэширование указателя на6
вa.cache(6)
недопустимо. - person Yakk - Adam Nevraumont   schedule 01.05.20176_L
может быть lvalue. Я мог бы сделать это более явным:6_lvalue
. - person Yakk - Adam Nevraumont   schedule 01.05.20176
. Такой указатель не может существовать, потому что6
является значением prvalue. Вы всегда сначала инициализируете объект из этого значения prvalue, а затем можете взять адрес этого объекта. Сам литерал не является объектом. C++17 делает это немного более очевидным благодаря улучшенным категориям значений, но в основном так оно и работало всегда. - person Kerrek SB   schedule 01.05.2017