[英]c++: generating string literals from template parameters
template < unsigned int i >
struct t {
static const char *s;
};
template < unsigned int i >
const char* t<i>::s = ...;
其中...
是“0 1 2 ... i-1”,例如i == 5
“0 1 2 3 4”。
這可能嗎? (請沒有在運行時執行此操作的解決方案!)
const
不會強制執行此操作,但可以采用任何運行時評估函數來生成字符串。這在技術上是可能的,只是非常非常丑陋。 這是一個為 unsigned int 生成字符串文字的示例。 它(尚未)創建形式為“1 2 3 ... i-1”的字符串,但是我相信如果您願意付出努力,這是可能的。
#include <iostream>
#include <string>
#include <limits>
///////////////////////////////////////////////////////////////////////////////
// exponentiation calculations
template <int accum, int base, int exp> struct POWER_CORE : POWER_CORE<accum * base, base, exp - 1>{};
template <int accum, int base>
struct POWER_CORE<accum, base, 0>
{
enum : int { val = accum };
};
template <int base, int exp> struct POWER : POWER_CORE<1, base, exp>{};
///////////////////////////////////////////////////////////////////////////////
// # of digit calculations
template <int depth, unsigned int i> struct NUM_DIGITS_CORE : NUM_DIGITS_CORE<depth + 1, i / 10>{};
template <int depth>
struct NUM_DIGITS_CORE<depth, 0>
{
enum : int { val = depth};
};
template <int i> struct NUM_DIGITS : NUM_DIGITS_CORE<0, i>{};
template <>
struct NUM_DIGITS<0>
{
enum : int { val = 1 };
};
///////////////////////////////////////////////////////////////////////////////
// Convert digit to character (1 -> '1')
template <int i>
struct DIGIT_TO_CHAR
{
enum : char{ val = i + 48 };
};
///////////////////////////////////////////////////////////////////////////////
// Find the digit at a given offset into a number of the form 0000000017
template <unsigned int i, int place> // place -> [0 .. 10]
struct DIGIT_AT
{
enum : char{ val = (i / POWER<10, place>::val) % 10 };
};
struct NULL_CHAR
{
enum : char{ val = '\0' };
};
///////////////////////////////////////////////////////////////////////////////
// Convert the digit at a given offset into a number of the form '0000000017' to a character
template <unsigned int i, int place> // place -> [0 .. 9]
struct ALT_CHAR : DIGIT_TO_CHAR< DIGIT_AT<i, place>::val >{};
///////////////////////////////////////////////////////////////////////////////
// Convert the digit at a given offset into a number of the form '17' to a character
// Template description, with specialization to generate null characters for out of range offsets
template <unsigned int i, int offset, int numDigits, bool inRange>
struct OFFSET_CHAR_CORE_CHECKED{};
template <unsigned int i, int offset, int numDigits>
struct OFFSET_CHAR_CORE_CHECKED<i, offset, numDigits, false> : NULL_CHAR{};
template <unsigned int i, int offset, int numDigits>
struct OFFSET_CHAR_CORE_CHECKED<i, offset, numDigits, true> : ALT_CHAR<i, (numDigits - offset) - 1 >{};
// Perform the range check and pass it on
template <unsigned int i, int offset, int numDigits>
struct OFFSET_CHAR_CORE : OFFSET_CHAR_CORE_CHECKED<i, offset, numDigits, offset < numDigits>{};
// Calc the number of digits and pass it on
template <unsigned int i, int offset>
struct OFFSET_CHAR : OFFSET_CHAR_CORE<i, offset, NUM_DIGITS<i>::val>{};
///////////////////////////////////////////////////////////////////////////////
// Integer to char* template. Works on unsigned ints.
template <unsigned int i>
struct IntToStr
{
const static char str[];
};
template <unsigned int i>
const char IntToStr<i>::str[] =
{
OFFSET_CHAR<i, 0>::val,
OFFSET_CHAR<i, 1>::val,
OFFSET_CHAR<i, 2>::val,
OFFSET_CHAR<i, 3>::val,
OFFSET_CHAR<i, 4>::val,
OFFSET_CHAR<i, 5>::val,
OFFSET_CHAR<i, 6>::val,
OFFSET_CHAR<i, 7>::val,
OFFSET_CHAR<i, 8>::val,
OFFSET_CHAR<i, 9>::val,
NULL_CHAR::val
};
///////////////////////////////////////////////////////////////////////////////
// Tests
int _tmain(int argc, _TCHAR* argv[])
{
std::wcout << IntToStr<17>::str << std::endl;
std::wcout << IntToStr<173457>::str << std::endl;
std::wcout << IntToStr< INT_MAX >::str << std::endl;
std::wcout << IntToStr<0>::str << std::endl;
std::wcout << IntToStr<1>::str << std::endl;
std::wcout << IntToStr<-1>::str << std::endl;
return 0;
}
不,但這是可能的:
template < unsigned int i >
struct t {
static std::string s;
static std::string ConvertIntToString()
{
std::stringstream ss;
ss << i;
return ss.str();
}
};
template< unsigned int i >
std::string t< i >::s = t<i>::ConvertIntToStr();
順便說一句,你為什么使用 c 字符串? C++ 有 std::string 類,這是優越的。
編輯
我想你可以使用模板專業化:
template < unsigned int i >
struct t;
template <>
struct t<0>
{
static const char * const s;
};
const char* const t<0>::s = "abc";
template <>
struct t<1>
{
static const char * const s;
};
const char* const t<1>::s = "123";
不可能。
因為模板的擴展是在編譯時完成的,編譯器只能處理它知道的常量值。 任何涉及內存分配(例如初始化字符串)的操作此時都不可能,而只能在運行時進行。
您提供的代碼,...
template < unsigned int i >
struct t {
static const char *s;
};
static const char* t::s = ...;
...無效。 t::s
必須有外部鏈接。 此外,定義需要模板化。
修復代碼的直接問題,例如...
template < unsigned int i >
struct T
{
static const char * const s;
};
template< unsigned i >
const char* const T<i>::s = ...;
...然后用任何所需的字符串初始化T<i>::s
是微不足道的。
因此,以您的代碼中的錯誤為模,答案是“是的,這不僅可能,而且微不足道”。
但是你為什么要讓這個 Rube Goldberg 計划完成一件微不足道的事情呢?
我認為使用可變參數模板可能是可行的。 我沒有要測試的編譯器,但我想像這樣的東西可能會奏效。
template < char ... RHS, unsigned int i>
struct t {
static const char s[] = t<' ', char(i+'0'), RHS, i-1>::s;
};
template <char ... RHS >
struct t<RHS, 0> {
static const char s[] = {'0', RHS, '\0'};
};
void main() {
std::cout << t<5>::s; // {'0',' ','1',' ','2',' ','3',' ','4',' ','5','\0'}
}
使用模板是不可能的。 但是使用stringstream
,創建這樣的string
是微不足道的。 這是偽代碼:
string makeit(int i)
{
stringstream sstr;
for (int x = 0; x < i-1; x++)
put x and ' ' in sstr;
put i in sstr;
return sstr contents converted to string
}
可以在此處找到有關stringstream
更多信息。
//using lambda
#include <sstream>
template<size_t i372> struct T369 {
static std::string s;
};
template<size_t i372> std::string T369<i372>::s = [](){std::stringstream ss;
for (int j = 0; j < i372; j++) { ss << "," << j; }; return ss.str(); }();
使用現代 C++,這現在是可能的。
我相信它可以用 C++17 來完成,但這個解決方案使用了一些 C++20 特性:
#include <iostream>
#include <concepts>
template <char... Cs>
struct char_pack {
using self = char_pack<Cs...>;
static constexpr size_t size = sizeof...(Cs);
private:
// This allows us to use ::concat on types that inherit from char_pack<...>,
// such as int_to_char_pack.
// We need this because Cs (below) can't be deduced from a derived type.
//
// Ex:
// char_pack<'a', 'b', 'c'>::concat<int_to_char_pack<123>>
template <typename Other>
struct concat_impl : concat_impl<typename Other::self> {};
template <char... OtherCs>
struct concat_impl<char_pack<OtherCs...>> : char_pack<Cs..., OtherCs...> {};
public:
// Using a type alias means we don't have to write ::self in
// certain places that we otherwise would have needed to due
// to some quirks in the template evaluation system.
template <typename Other>
using concat = concat_impl<Other>;
template <char... OtherCs>
using append = char_pack<Cs..., OtherCs...>;
static constexpr const char to_string[size + 1] = {Cs..., '\0'};
};
template <auto I>
struct int_to_char_pack : char_pack<> {};
template <std::integral IT, IT I>
requires(I >= 10)
struct int_to_char_pack<I> : int_to_char_pack<I / 10>::append<'0' + (I % 10)> {};
template <std::integral IT, IT I>
requires(I < 10 && I >= 0)
struct int_to_char_pack<I> : char_pack<'0' + (I % 10)> {};
template <std::integral IT, IT I>
requires(I < 0)
struct int_to_char_pack<I> : char_pack<'-'>::concat<int_to_char_pack<-I>> {};
template <int I>
struct num_list : num_list<I - 1>::append<' '>::concat<int_to_char_pack<I>> {};
template <>
struct num_list<0> : char_pack<'0'> {};
int main() {
std::cout << num_list<10>::to_string;
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.