将字符串文字作为参数传递给C++模板类

Maw*_*awg 39 c++ templates

我想要一个在其构造函数中包含两个参数的类.第一个可以是int,double或float,所以<typename T>,第二个总是字符串文字"my string",所以我想const char * const.

任何人都可以给我一些可编译的代码,声明一个简单的类模板,并声明该类的对象?

谢谢

ami*_*mit 37

进一步来自Neil的答案:根据需要使用带有模板的字符串的一种方法是定义traits类并将字符串定义为类型的特征.

#include <iostream>

template <class T>
struct MyTypeTraits
{
   static const char* name;
};

template <class T>
const char* MyTypeTraits<T>::name = "Hello";

template <>
struct MyTypeTraits<int>
{
   static const char* name;
};

const char* MyTypeTraits<int>::name = "Hello int";

template <class T>
class MyTemplateClass
{
    public:
     void print() {
         std::cout << "My name is: " << MyTypeTraits<T>::name << std::endl;
     }
};

int main()
{
     MyTemplateClass<int>().print();
     MyTemplateClass<char>().print();
}
Run Code Online (Sandbox Code Playgroud)

版画

My name is: Hello int
My name is: Hello
Run Code Online (Sandbox Code Playgroud)


Moo*_*uck 22

可以使用const char*非类型模板参数,并将其传递给const char[]带有static链接的变量,该变量与直接传递字符串文字的距离并不远.

#include <iostream>    

template<const char *str> 
struct cts {
    void p() {std::cout << str;}
};

static const char teststr[] = "Hello world!";
int main() {
    cts<teststr> o;
    o.p();
}
Run Code Online (Sandbox Code Playgroud)

http://coliru.stacked-crooked.com/a/64cd254136dd0272

  • 这应该是固定答案.我希望在我自己发现之前我没有错过这个...比我需要的时间晚了一个月. (3认同)
  • 全局变量也有明确定义的标识;字符串文字,没有那么多(“hello”和“hello”是同一个对象吗)? (2认同)
  • @curiousguy:“有时”,但你是对的,这是一个风险/怪癖 (2认同)
  • constexpr char teststr[] = "Hello world!"; 看起来也不错 (2认同)

小智 21

抱歉,C++目前不支持使用字符串文字(或真正的文字)作为模板参数.

但重新阅读你的问题,那就是你在问什么?你不能说:

foo <"bar"> x;
Run Code Online (Sandbox Code Playgroud)

但你可以说

template <typename T>
struct foo {
   foo( T t ) {}
};

foo <const char *> f( "bar" );
Run Code Online (Sandbox Code Playgroud)


Cir*_*四事件 19

C++20 fixed_string+“非类型模板参数中的类类型”

显然,对此的提议首先被接受,但随后被删除:“字符串文字作为非类型模板参数”

删除的部分原因是它被认为很容易处理另一个被接受的提案:“非类型模板参数中的类类型”

接受的提案包含一个具有以下语法的示例:

template <std::basic_fixed_string Str>
struct A {};
using hello_A = A<"hello">;
Run Code Online (Sandbox Code Playgroud)

一旦我看到支持它的编译器,我将尝试用一个示例来更新它,该示例实际上告诉我任何事情。

Redditor还表明以下内容可以在 GCC master 上编译,前提是您定义自己的版本,basic_fixed_string该版本尚未在标准库中:https : //godbolt.org/z/L0J2K2

template<unsigned N>
struct FixedString {
    char buf[N + 1]{};
    constexpr FixedString(char const* s) {
        for (unsigned i = 0; i != N; ++i) buf[i] = s[i];
    }
    constexpr operator char const*() const { return buf; }
};
template<unsigned N> FixedString(char const (&)[N]) -> FixedString<N - 1>;

template<FixedString T>
class Foo {
    static constexpr char const* Name = T;
public:
    void hello() const;
};

int main() {
    Foo<"Hello!"> foo;
    foo.hello();
}
Run Code Online (Sandbox Code Playgroud)

g++ -std=c++2a来自 Ubuntu PPA 的9.2.1无法编译:

/tmp/ccZPAqRi.o: In function `main':
main.cpp:(.text+0x1f): undefined reference to `_ZNK3FooIXtl11FixedStringILj6EEtlA7_cLc72ELc101ELc108ELc108ELc111ELc33EEEEE5helloEv'
collect2: error: ld returned 1 exit status
Run Code Online (Sandbox Code Playgroud)

参考书目:https : //botondballo.wordpress.com/2018/03/28/trip-report-c-standards-meeting-in-jacksonville-march-2018/

最后,EWG 决定撤消先前批准的提案,以允许在非类型模板参数中使用字符串文字,因为允许在非类型模板参数中使用类类型的更通用的工具(刚刚批准)是一个足够好的替代品。(这与上次会议相比有所改变,当时看起来我们都想要。)主要区别在于您现在必须将字符数组包装到一个结构中(想想 fixed_string 或类似的),并将其用作模板参数类型。(P0424 的用户自定义字面量部分仍在继续,对允许的模板参数类型进行了相应的调整。)

这在 C++17 中会特别酷if constexpr在 C++ 中编译时使用 if/else?

这种特性似乎符合 C++20 中令人敬畏的“constexpr 一切”提案,例如:是否可以在 constexpr 中使用 std::string?

  • 根据 21 年 8 月,以下编译器支持: msvc v19.29 clang 12.0 gcc 9.1 icx 2021.2.0 (2认同)

Alw*_*ing 11

inline const wchar_t *GetTheStringYouWant() { return L"The String You Want"; }

template <const wchar_t *GetLiteralFunc(void)>
class MyType
{
     void test()
     {
           std::cout << GetLiteralFunc;
     }    
}

int main()
{
     MyType<GetTheStringYouWant>.test();
}
Run Code Online (Sandbox Code Playgroud)

尝试使用函数的地址作为模板参数.


Mon*_*omy 11

这是MPLLIBS将字符串作为模板参数传递的解决方案(C++ 11).

#include <iostream>
#include <mpllibs/metaparse/string.hpp> // https://github.com/sabel83/mpllibs
#include <boost/mpl/string.hpp>

// -std=c++11

template<class a_mpl_string>
struct A
{
  static const char* string;
};

template<class a_mpl_string>
const char* A< a_mpl_string >
::string { boost::mpl::c_str< a_mpl_string >::value };  // boost compatible

typedef A< MPLLIBS_STRING ( "any string as template argument" ) > a_string_type;

int main ( int argc, char **argv )
{
  std::cout << a_string_type{}.string << std::endl;
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

打印:

any string as template argument
Run Code Online (Sandbox Code Playgroud)

github上的lib:https://github.com/sabel83/mpllibs


ami*_*mit 6

根据您在Niel答案下的评论,另一种可能性如下:

#include <iostream>

static const char* eventNames[] = { "event_A", "event_B" };

enum EventId {
        event_A = 0,
        event_B
};

template <int EventId>
class Event
{
public:
   Event() {
     name_ = eventNames[EventId];
   }
   void print() {
        std::cout << name_ << std::endl;
   }
private:
   const char* name_;
};

int main()
{
        Event<event_A>().print();
        Event<event_B>().print();
}
Run Code Online (Sandbox Code Playgroud)

版画

event_A
event_B
Run Code Online (Sandbox Code Playgroud)

  • 我将使用#运算符的枚举值来生成字符串表示. (2认同)

Gre*_*osz 5

编辑:好的,您的问题标题似乎具有误导性

“我想要一个在其构造函数中带有两个参数的类。第一个可以是 int、double 或 float,所以,第二个始终是字符串文字“我的字符串”,所以我猜是 const char * const。”

看起来您正在努力实现:

template<typename T>
class Foo
{
  public:
  Foo(T t,  const char* s) : first(t), second(s)
  {
    // do something
  }

  private:
  T first;
  const char* second;

};
Run Code Online (Sandbox Code Playgroud)

这适用于任何类型,对于第一个参数:int, float, double, 等等。

现在,如果您真的想将第一个参数的类型限制为仅int,floatdouble; 你可以想出一些更精细的东西,比如

template<typename T>
struct RestrictType;

template<>
struct RestrictType<int>
{
  typedef int Type;
};

template<>
struct RestrictType<float>
{
  typedef float Type;
};

template<>
struct RestrictType<double>
{
  typedef double Type;
};

template<typename T>
class Foo
{
  typedef typename RestrictType<T>::Type FirstType;

  public:
  Foo(FirstType t,  const char* s) : first(t), second(s)
  {
    // do something
  }

  private:
  FirstType first;
  const char* second;

};

int main()
{
  Foo<int> f1(0, "can");
  Foo<float> f2(1, "i");
  Foo<double> f3(1, "have");
  //Foo<char> f4(0, "a pony?");
}
Run Code Online (Sandbox Code Playgroud)

如果您删除最后一行的注释,您实际上会得到一个编译器错误。


C++2003 不允许字符串文字

ISO/IEC 14882-2003 §14.1:

14.1 模板参数

非类型模板参数应具有以下(可选的 cv 限定)类型之一:

— 整数或枚举类型,

— 指向对象或指向函数的指针,

— 对对象的引用或对函数的引用,

— 指向成员的指针。

ISO/IEC 14882-2003 §14.3.2:

14.3.2 模板非类型参数

非类型、非模板模板参数的模板参数应为以下之一:

— 整数或枚举类型的整数常量表达式;或者

— 非类型模板参数的名称;或者

— 具有外部链接的对象或函数的地址,包括函数模板和函数模板 ID,但不包括非静态类成员,表示为 & id 表达式,其中 & 是可选的,如果名称指的是函数或数组,或者如果相应的模板参数是一个引用;或者

— 指向如 5.3.1 中描述的成员的指针。

[注意:字符串文字 (2.13.4) 不满足任何这些类别的要求,因此不是可接受的模板参数。

[例子:

template<class T, char* p> class X { 
  //... 
  X(); 
  X(const char* q) { /* ... */ } 
}; 

X<int,"Studebaker"> x1; //error: string literal as template-argument 
char p[] = "Vivisectionist"; 
X<int,p> x2; //OK 
Run Code Online (Sandbox Code Playgroud)

—结束示例] —结束说明]

而且看起来在即将到来的 C++0X 中它不会改变,请参阅当前草案 14.4.2 Template non-type arguments


Man*_*ork 5

您不能直接将字符串文字作为模板参数传递。

但是您可以接近:

template<class MyString = typestring_is("Hello!")>
void MyPrint() {
  puts( MyString::data() );
}

...
// or:
MyPrint<typestring_is("another text")>();
...
Run Code Online (Sandbox Code Playgroud)

您需要的只是这里的一个小头文件。


备择方案:

  • 定义一个全局char const *变量并将其作为指针传递给模板。(这里

    缺点:需要模板参数列表之外的其他代码。如果您需要指定字符串文字“ inline”,则不合适。

  • 使用非标准语言扩展名。(这里

    缺点:不能保证与所有编译器一起使用。

  • 使用BOOST_METAPARSE_STRING。(这里

    缺点:您的代码将取决于Boost库。

  • 使用char的可变参数模板参数包,例如str_t<'T','e','s','t'>

    这就是以上解决方案在后台为您执行的操作。


Ant*_*nyk 5

使用代理static constexpr const char type_name_str[] = {"type name"};将字符串作为模板参数传递。定义字符串使用[]很重要。

#include <iostream>

template<typename T, const char* const t_name>
struct TypeName
{
public:

    static constexpr const char* Name()         
    {                                   
        return t_name;
    };                                  

};

static constexpr const char type_name_str[] = {"type name"};

int main() 
{
    std::cout<<TypeName<float, type_name_str>::Name();
    return 0;
}
Run Code Online (Sandbox Code Playgroud)