template < unsigned int i >
struct t {
static const char *s;
};
template < unsigned int i >
const char* t<i>::s = ...;
其中...
为“0 1 2 ... i-1”,例如“0 1 2 3 4”为i == 5
。
这可能吗? (请不要在运行时做这个解决方案!)
const
不会强制执行此操作,但可以对字符串生成采用任何运行时评估函数。答案 0 :(得分:6)
这在技术上是可行的,它非常非常难看。这是一个为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;
}
答案 1 :(得分:3)
不,但这是可能的:
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();
btw你为什么使用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";
答案 2 :(得分:1)
不可能。
因为模板的扩展是在编译时完成的,当编译器只能处理它知道的常量值时。任何涉及内存分配的操作(例如,初始化字符串)目前都不可能,但仅限于运行时。
答案 3 :(得分:1)
您要呈现的代码,......
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计划完成一件小事?
答案 4 :(得分:1)
我认为可能可以使用可变参数模板。我没有一个可以测试的编译器,但是我想象一下这个可能的工作。
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'}
}
答案 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
can be found here的更多信息。
答案 6 :(得分:0)
//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(); }();
答案 7 :(得分:0)
使用现代 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;
}