Cスタイルの文字列をテンプレート引数として使用できますか?
私は試した:
template <char *str>
struct X
{
const char *GetString() const
{
return str;
}
};
int main()
{
X<"String"> x;
cout<<x.GetString();
}
また、クラス定義について不満はありませんが、インスタンス化により'X' : invalid expression as a template argument for 'str'
(VC)。
更新:最近、この質問に答えてから数年後、文字列リテラルをテンプレート引数として使用することが可能になりました。 C++ 11では、文字パックをテンプレート引数として使用できます(template<char ...c>
)および 可能 このようなテンプレートにリテラル文字列を渡す。
ただし、これは機能します。
template <char const *str>
struct X
{
const char *GetString() const
{
return str;
}
};
char global_string[] = "String";
int main()
{
X<global_string> x;
cout<<x.GetString();
}
このような古い質問を投稿して申し訳ありませんが、ストレージを使用せずにリテラルを引数として実際に渡す最もクリーンなアプローチは次のとおりです。
文字列をタイプとしてエンコードします。
template <char... chars>
using tstring = std::integer_sequence<char, chars...>;
ユーザー定義のリテラル演算子を作成します。
template <typename T, T... chars>
constexpr tstring<chars...> operator""_tstr() { return { }; }
そして、必要に応じて部分的な特殊化を使用して文字データを回復します。
template <typename>
struct X;
template <char... elements>
struct X<tstring<elements...>> {
const char* GetString() const
{
static constexpr char str[sizeof...(elements) + 1] = { elements..., '\0' };
return str;
}
};
これにより、以下を作成できます。
X<decltype("my_string"_tstr)>
ユーザー定義リテラルは、C++ 14ではない非標準( n3599 )機能を使用しますが、最近のGCCおよびClangビルドでサポートされており、C++ 1zで再検討されることを期待しています。
私は知っていましたが、このトピックは少し古いですが、誰かが興味を持っている場合はこのコメントを入れました。マクロの組み合わせでリテラル文字列を引数として渡すテンプレートを実現しました。
コード例を作成しましたが、
#include <stdio.h>
#include <iostream>
#include <vector>
#include <memory>
#include <string.h>
using namespace std;
#define MAX_CONST_CHAR 100
#define MIN(a,b) (a)<(b)?(a):(b)
#define _T(s)\
getChr(s,0),\
getChr(s,1),\
getChr(s,2),\
getChr(s,3),\
getChr(s,4),\
getChr(s,5),\
getChr(s,6),\
getChr(s,7),\
getChr(s,8),\
getChr(s,9),\
getChr(s,10),\
getChr(s,11),\
getChr(s,12),\
getChr(s,13),\
getChr(s,14),\
getChr(s,15),\
getChr(s,16),\
getChr(s,17),\
getChr(s,18),\
getChr(s,19),\
getChr(s,20),\
getChr(s,21),\
getChr(s,22),\
getChr(s,23),\
getChr(s,24),\
getChr(s,25),\
getChr(s,26),\
getChr(s,27),\
getChr(s,28),\
getChr(s,29),\
getChr(s,30),\
getChr(s,31),\
getChr(s,32),\
getChr(s,33),\
getChr(s,34),\
getChr(s,35),\
getChr(s,36),\
getChr(s,37),\
getChr(s,38),\
getChr(s,39),\
getChr(s,40),\
getChr(s,41),\
getChr(s,42),\
getChr(s,43),\
getChr(s,44),\
getChr(s,45),\
getChr(s,46),\
getChr(s,47),\
getChr(s,48),\
getChr(s,49),\
getChr(s,50),\
getChr(s,51),\
getChr(s,52),\
getChr(s,53),\
getChr(s,54),\
getChr(s,55),\
getChr(s,56),\
getChr(s,57),\
getChr(s,58),\
getChr(s,59),\
getChr(s,60),\
getChr(s,61),\
getChr(s,62),\
getChr(s,63),\
getChr(s,64),\
getChr(s,65),\
getChr(s,66),\
getChr(s,67),\
getChr(s,68),\
getChr(s,69),\
getChr(s,70),\
getChr(s,71),\
getChr(s,72),\
getChr(s,72),\
getChr(s,72),\
getChr(s,73),\
getChr(s,74),\
getChr(s,75),\
getChr(s,76),\
getChr(s,77),\
getChr(s,78),\
getChr(s,79),\
getChr(s,80),\
getChr(s,81),\
getChr(s,82),\
getChr(s,83),\
getChr(s,84),\
getChr(s,85),\
getChr(s,86),\
getChr(s,87),\
getChr(s,88),\
getChr(s,89),\
getChr(s,90),\
getChr(s,91),\
getChr(s,92),\
getChr(s,93),\
getChr(s,94),\
getChr(s,95),\
getChr(s,96),\
getChr(s,97),\
getChr(s,98),\
getChr(s,99),\
getChr(s,100)
#define getChr(name, ii) ((MIN(ii,MAX_CONST_CHAR))<sizeof(name)/sizeof(*name)?name[ii]:0)
template <char... Chars_>
class E {
public:
string *str;
E(){
std::vector<char> vec = {Chars_...};
str = new string(vec.begin(),vec.end());
}
~E()
{
delete str;
}
};
int main(int argc, char *argv[])
{
E<_T("Any template can pass const strings literals")> e;
printf("%s",e.str->c_str());
}
これは、g ++ 4.6と引数-std = c ++ 0xを渡すことで機能し、100文字の制限がありますが、もちろん、必要なだけ大きくすることができます。たぶん、この手法は十分に最適化されていないかもしれませんが、必要な外部変数を宣言するよりも生産的です(きっと;))
制約:リテラル文字列は、可変引数を渡すため、テンプレートの最後の引数でなければなりません。
[〜#〜] edit [〜#〜]:Padekのおかげで、彼はこのコードがVisual Studio 2017でも動作することをテストしましたが、strlen by sizeof(name)/ sizeof(* name)。
C++ 11を使用すると、文字列リテラルを可変長テンプレート引数、つまりintテンプレートパラメーターのコレクションとしてかなり正常に表現できます。そのような文字列ごとに手動でfoo<16, 73, 51 ...>
を記述することなく、そのようなテンプレートを1つセットアップする概念実証の例をまとめました。
例:
// The template we want to pass a string to
template <int... Args>
struct foo {
// It needs one helper function for decltype magic, this could be avoided though
template <int N>
static foo<N, Args...> add_one();
};
// This is the string we want to use with foo, simulating foo<"Hello world!" __FILE__>:
constexpr const char *teststr = "Hello world!" __FILE__;
// Get char N of a string literal
constexpr int strchr(const char *str, int N) { return str[N]; }
// recursive helper to build the typedef from teststr
template <int N, int P=0>
struct builder {
typedef typename builder<N, P+1>::type child;
typedef decltype(child::template add_one<strchr(teststr,P)>()) type;
};
template <int N>
struct builder<N,N> {
typedef foo<strchr(teststr, N)> type;
};
// compile time strlen
constexpr int slen(const char *str) {
return *str ? 1 + slen(str+1) : 0;
}
int main() {
builder<slen(teststr)>::type test;
// compile error to force the type to be printed:
int foo = test;
}
constexpr
には少なくともgcc 4.6が必要であり、それでも多少の洗練を使用できますが、コンパイラエラーは、型が正常に構築されていることを示しています。
error: cannot convert ‘builder<19>::type {aka foo<72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 33, 115, 108, 105, 116, 46, 99, 99, 0>}’ to ‘int’ in initializatio
いいえ、コンパイル時に文字列リテラルを操作することはできません。あなたが得ることができる最高のものは、奇妙な複数文字リテラルです(例:'abcd'
)いくつかのコンパイル時パーサーが使用します。それらは§2.13.2.1で言及されています:
複数のc-charを含む通常の文字リテラルは、複数文字リテラルです。複数文字リテラルには、型intと実装定義の値があります。
C++ 0xでは、この制限を回避する方法があるかもしれませんが、 新しい文字列リテラル で、 Arctic Interactive に興味深い記事があります。
いいえ。C++標準14.3.2/1によると:
タイプ、テンプレート以外のテンプレートパラメータのテンプレート引数は、次のいずれかです。
—整数型または列挙型の整数定数式。または
—非タイプテンプレートパラメーターの名前。または
[。関数または配列、または対応するテンプレートパラメータが参照の場合;または
— 5.3.1で説明されているように表現されたメンバーへのポインター。
文字列はリストにありません。
文字列のアドレスをテンプレートパラメータとして外部リンケージとともに使用できます。例:
template <const char** T> class My {
public:
void do_stuff() {
std::cout << "zzz";
}
};
const char* str;
int main()
{
My<&str> zz;
zz.do_stuff();
printf("Result: %d %d \n", 60 % 10 + 1, (60 % 10 ) + 1 );
}