C++模版元編程中如何拼接兩個const char*?
例如:
template&struct Strcat{ static constexpr Value=...//這裡該怎麼寫?
};//main()std::cout&<&::Value;
如果利用宏,上面有別人貼過的爆棧的link應該夠用了
如果潔癖不利用宏,必須先把const char[]類型的string literal轉為可以其他類型,以便下手。
原因是C++裡面built-in數組沒有constexpr的構造函數,僅有{}形式一種(修正:這裡其實是因為gcc一個bug導致數組的built-in copy constructor不被認為constexpr,clang是支持的)這裡為了保證構造函數constexpr,取巧利用了一個C++規範外,但是基本是事實標準的class layout,即單一繼承情況下,base class的成員在derived class的成員之前。
template &
struct String : String&
constexpr String(const char(str)[N]) : String&
protected:
template &
constexpr String(const char(str)[M]) : String&
char c;
};
template &<&>
struct String&<0&> {
template &
constexpr String(T...) {}
};
這樣進行任何String的實例即可生成一個memory layout和const char[]一致的實例
如String&<6&>("hello")。
可以用個輔助函數來使用時避開模板參數template &
constexpr String&
constexpr String&<0&> mkString(const char(str)[0]) { return String&<0&>(str);}
這樣使用時只需要mkString("hello")即可。
然後有了這個String類,想拼接就很容易啦,我這裡上一個偷懶的版本,即String&template &
struct StringCat : String&
{
T t;
constexpr StringCat(const char(str)[M], const T str2) : String&
};
template & 這樣使用時,只要直接用auto + catString即可
constexpr StringCat&
return StringCat&
}
const char hello[] = "hello, ";
const char world[] = "world!!";
//這裡hello和world聲明為array而不是pointer
//是因為String類的構造函數做了保護性檢查,public構造函數僅接受const char [SIZE]的array
//可以修改String類的構造函數的參數為const char *
//來使用const char *類型的字元常量
const auto helloworld = catString(hello, world);
生成彙編如下
helloworld:
.byte 104
.byte 101
.byte 108
.byte 108
.byte 111
.byte 44
.byte 32
.byte 119
.byte 111
.byte 114
.byte 108
.byte 100
.byte 33
.byte 33
.byte 0
需要const char*的話,reinterpret_cast&
這個問題非常有意思,C++模版元編程中如何拼接兩個const char*?這個答案我是支持的,Template recursion的問題是
- 出來的類都是奇葩類,而且重要的是你concat結束了之後很有可能作為一個參數要傳到某個函數裡面的,這個函數一般不會說設計的接受這些個奇葩類的,那你這時候還得寫各種cast。
- 其實folly::fatal 的reflection也是用這裡的回答解決的問題,而且fb現在所有的thrift reflection走fatal的話都是像之前回答裡面一樣做compile time concatenation的,問題是這樣用template解決問題的時候binary文件各種爆炸
前幾天一個同事Eric Niebler終於妥善解決了這個問題(commit在這裡add folly::FixedString, a constexpr-usable string with a fixed-size i… · facebook/folly@77d5e54)(其實之前有個東西叫folly::StringPiece類似大家的答案,要是大家有興趣的話我可以補充),寫了一個東西叫folly::FixedString,基本上就是akirya 的答案加上無數的cast/helper 函數,還支持replace。比如
constexpr auto hello = makeFixedString("hello"); // a FixedString&<5&>
constexpr auto world = makeFixedString("world"); // another FixedString&<5&>
constexpr auto hello_world = hello + " " + world + "!";
static_assert(hello_world == "hello world!", "w00t");
EXPECT_STREQ("hello world!", hello_world.c_str());
還比如
FixedString&<10&> test{"****"};
test.replace(1, 2, "!!!!");
EXPECT_STREQ("*!!!!*", test.c_str());
static_assert(makeFixedString("****").creplace(1, 2, "!!!!") == "*!!!!*", "");
很遺憾不能,C++語言層沒有提供區分字元串字面值和const char *以及char[]的機制,必須用宏
貌似在哪裡見到過。也思考過。但沒實際寫過。
可能這樣想的:可以遞歸兩個字元串,遇到0結束。用一個類裝一個靜態char數組 (長度由上面的遞歸推導)。再遞歸的把結果放入這個類的靜態char數組成員里。
但最後一步做不到,那是運行時。
能做到的是保存結果到一個字元串的另外一種形式(用一個繼承的類型來表示,類似tuple)如果是常量字元串的話,時可以拼接成std::array&
#include&
#include&
#include&
#include&
#include&
using std::cout;
using std::endl;
// strcat on Compile-time
namespace detail
{
namespace detail
{
template &
constexpr std::array&
to_array_impl( T( a )[N] , std::index_sequence&
{
return{ { a[I]... } };
}
}
template &
constexpr std::array&
{
return detail::to_array_impl( a , std::make_index_sequence&
}
template &
constexpr std::array&
{
return{ {c,0} };
}
template &
constexpr std::array &< T , N1 + N2 - 1 &>
concat_string_impl( const std::array&
{
return{ { a[I1]... , b[I2]... } };
}
template &
constexpr std::array &< T , N1 + N2 - 1 &>
concat_string_impl( const std::array&
{
typedef std::make_index_sequence &< N1 - 1 &> type1;
typedef std::make_index_sequence&
return concat_string_impl( a , type1() , b , type2() );
}
template &
constexpr auto
concat_string_impl( const std::array&
{
typedef std::make_index_sequence &< N1 - 1 &> type1;
typedef std::make_index_sequence&
return concat_string_impl( concat_string_impl( a , type1() , b , type2() ) , args... );
}
}
template&
constexpr auto concat_string( Args... args )
{
return detail::concat_string_impl( detail::to_array( args )... );
}
template&
void outputArray( const std::array&
{
cout &<&< typeid( value ).name() &<&< " " &<&< value.data() &<&< endl;
}
constexpr auto a = concat_string( "11111abc" , "1" , "ABC" , "!!!" ,"%" );
constexpr auto b = concat_string( "abc" , "1234" );
int main()
{
outputArray( concat_string( "abc" , "1234" ) );
outputArray( concat_string( "a" , "1234" ) );
outputArray( concat_string( "abc" , "1" ) );
outputArray( concat_string( "abc" , "1" , "ABC" , "!!!" ,"%" ) );
return 0;
}
這…沒問題?
如果是literal string的話…… 直接拼起來就行了
#define COMBINE_PSTR(str1,str2) str1##str2編譯時期拼接const char*還是有可能的,見How to concatenate a const char* in compile time但是語法不允許string literal直接作為模板參數吧。
模板的感覺單就const char *不夠,我來作弊一下,給出一個宏版本的,也是編譯期完成字元串連接的。
#include&
#define STRCAT(STRING_L,STRING_R) STRING_L STRING_R
template &
void get_str_information(const char (str)[length])
{
std::cout &<&< "the string is " &<&< str &<&< std::endl;
std::cout &<&< "the length is " &<&< length&<&
不懂為啥strcat還能擼模板。如果是偏特化const char*的話可以使用char *str=const_cast&
推薦閱讀:
※windows有沒有類似xcode的軟體,寫完代碼可以run一下?
※已經邁入30的程序員,還能幹幾年,以後該怎麼辦呢?
※C語言兩數定義正確,相乘溢出的原因?
※做 C 語言編譯器前端的難度如何?