C 样式字符串作为模板参数?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1826464/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
C-Style Strings as template arguments?
提问by cvb
Can C-Style strings be used as template arguments?
C 样式字符串可以用作模板参数吗?
I tried:
我试过:
template <char *str>
struct X
{
const char *GetString() const
{
return str;
}
};
int main()
{
X<"String"> x;
cout<<x.GetString();
}
And although I get no complaints about the class definition, the instantiation yields 'X' : invalid expression as a template argument for 'str'
(VC).
尽管我对类定义没有任何抱怨,但实例化产生了'X' : invalid expression as a template argument for 'str'
(VC)。
采纳答案by moonshadow
A string literal cannot be used as a template argument.
Update: Nowadays, a few years after this question was asked and answered, it is possible to use string literals as template arguments. With C++11, we can use characters packs as template arguments (template<char ...c>
) and it is possibleto pass a literal string to such a template.
更新:如今,在提出并回答这个问题几年后,可以使用字符串文字作为模板参数。在 C++11 中,我们可以使用字符包作为模板参数 ( template<char ...c>
),并且可以将文字字符串传递给这样的模板。
This would work, however:
但是,这会起作用:
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();
}
回答by Matt Bierner
Sorry to post on such an old question, but here's what I feel is the cleanest approach to actually pass a literal as the argument without using storage.
很抱歉发布这样一个老问题,但我认为这是在不使用存储的情况下实际将文字作为参数传递的最简洁方法。
Encode the string as a type:
将字符串编码为类型:
template <char... chars>
using tstring = std::integer_sequence<char, chars...>;
Create a user defined literal operator:
创建一个用户定义的文字运算符:
template <typename T, T... chars>
constexpr tstring<chars...> operator""_tstr() { return { }; }
And use partial specialization to recover the character data as needed:
并根据需要使用偏特化来恢复字符数据:
template <typename>
struct X;
template <char... elements>
struct X<tstring<elements...>> {
const char* GetString() const
{
static constexpr char str[sizeof...(elements) + 1] = { elements..., 'X<decltype("my_string"_tstr)>
' };
return str;
}
};
This allows you to write:
这允许您编写:
#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());
}
The user defined literal uses non-standard (n3599) functionality not in C++14 but that is supported by recent GCC and Clang builds, and hopefully will be reconsidered for C++1z.
用户定义的文字使用非标准 ( n3599) 功能,不在 C++14 中,但最近的 GCC 和 Clang 版本支持该功能,希望 C++1z 会重新考虑。
回答by Jordi Espada
I known, this topic is a bit old but I put this comment if anyone is interested. I achieved templates with passing literal string as argument with combination of MACROS.
我知道,这个话题有点老了,但如果有人感兴趣,我会发表这个评论。我通过将文字字符串作为参数与宏的组合来实现模板。
I made a code example,
我做了一个代码示例,
// 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;
}
This works with g++ 4.6 and passing argument -std=c++0x, and have a limit of 100 char but, of course, can be as greater as you want. Maybe this technique is not well optimized, but it will be more productive than declare the needed external variables (I'm sure ;) )
这适用于 g++ 4.6 并传递参数 -std=c++0x,并且限制为 100 个字符,但当然,可以根据需要增加。也许这种技术没有得到很好的优化,但它会比声明所需的外部变量更有效率(我敢肯定;))
Constraints: The literal string must be one and last argument of the template due the passing of variadics arguments.
约束:由于传递了可变参数参数,文字字符串必须是模板的一个也是最后一个参数。
EDIT: Thanks to Padek he tested that this piece of code also it works with Visual Studio 2017 but changing strlenby sizeof(name)/sizeof(*name).
编辑:感谢Padek,他测试了这段代码也适用于Visual Studio 2017,但通过sizeof(name)/sizeof(*name)更改了strlen。
回答by Flexo
With C++11 you can fairly sanely represent string literals as variadic template arguments, i.e. a collection of int template parameters. I've put together a proof of concept example that sets up one such template without manually having to write foo<16, 73, 51 ...>
for every such string.
使用 C++11,您可以相当理智地将字符串文字表示为可变参数模板参数,即 int 模板参数的集合。我已经整理了一个概念证明示例,该示例设置了一个这样的模板,而不必foo<16, 73, 51 ...>
为每个这样的字符串手动编写。
Example:
例子:
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
You'll need at least gcc 4.6 for constexpr
and it could use some polish still but the compiler error I get indicates the type is being built sanely:
您至少需要 gcc 4.6constexpr
并且它仍然可以使用一些润色,但我得到的编译器错误表明该类型正在构建:
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 );
}
回答by Georg Fritzsche
No, you can't work with string literals at compile time. The best you can get are the weird multicharacter literals (e.g. 'abcd'
) which some compile-time parsers use. They are mentioned in §2.13.2.1:
不,您不能在编译时使用字符串文字。你能得到的最好的是'abcd'
一些编译时解析器使用的奇怪的多字符文字(例如)。它们在§2.13.2.1中提到:
An ordinary character literal that contains more than one c-char is a multicharacter literal. A multicharac- ter literal has type int and implementation-defined value.
包含多个 c-char 的普通字符文字是多字符文字。多字符文字具有 int 类型和实现定义的值。
In C++0x there might be ways around this limitations though with the new string literals, Arctic Interactivehas an interesting article on that.
在 C++0x 中,虽然有了新的字符串文字,但可能有办法绕过这个限制,Arctic Interactive有一篇有趣的文章。
回答by Kirill V. Lyadvinsky
No. According to C++ Standard 14.3.2/1:
否。根据 C++ 标准 14.3.2/1:
A template-argument for a non-type, non-template template-parameter shall be one of:
— an integral constant-expression of integral or enumeration type; or
— the name of a non-type template-parameter; or
— the address of an object or function with external linkage, including function templates and function template-ids but excluding non-static class members, expressed as & id-expression where the & is optional if the name refers to a function or array, or if the corresponding template-parameter is a reference;or
— a pointer to member expressed as described in 5.3.1 .
非类型、非模板模板参数的模板参数应为以下之一:
— 整数或枚举类型的整数常量表达式;或
— 非类型模板参数的名称;或
— 具有外部链接的对象或函数的地址,包括函数模板和函数模板 ID,但不包括非静态类成员,表示为 & id-expression 如果名称指的是函数或数组,则 & 是可选的,或者如果相应的模板参数是一个引用;或者
——一个指向成员的指针,如 5.3.1 中所述。
Strings are not in the list.
字符串不在列表中。
回答by corvus
You can use address of string with external linkage as a template parameter, e.g.:
您可以使用带有外部链接的字符串地址作为模板参数,例如:
##代码##回答by ctron
C++ does not know about strings. It only knowns about "arrays of characters" and there the literal would be the pointer to the array. So if you would use the "value" of your string as template parameter you would actually use the pointer value.
C++ 不知道字符串。它只知道“字符数组”,并且文字将是指向数组的指针。因此,如果您将字符串的“值”用作模板参数,您实际上会使用指针值。