7
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并不强制,但可以使用任何运行时评估函数来生成字符串。
4

8 回答 8

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;
}
于 2010-11-04T20:29:38.563 回答
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();

顺便说一句,你为什么使用 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";
于 2010-11-04T11:27:29.983 回答
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'}
}
于 2011-01-14T23:22:43.543 回答
1

不可能的。

因为模板的扩展是在编译时完成的,编译器只能处理它知道的常量值。任何涉及内存分配的操作(例如初始化字符串)此时都无法进行,只能在运行时进行。

于 2010-11-04T11:37:54.193 回答
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是微不足道的。

因此,以代码中的错误为模,答案是“是的,这不仅是可能的,而且是微不足道的”。

但是你为什么要这个鲁布戈德堡方案完成一件微不足道的事情呢?

于 2010-11-04T11:51:33.837 回答
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 可以在这里找到

于 2010-11-04T11:44:44.937 回答
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(); }();
于 2018-05-12T23:10:46.000 回答
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;
}
于 2021-01-02T18:51:34.173 回答