簡體   English   中英

將字符串文字作為類型參數傳遞給 class 模板

[英]Passing a string literal as a type argument to a class template

我想聲明一個 class 模板,其中一個模板參數采用字符串文字,例如my_class<"string">

任何人都可以給我一些可編譯的代碼來聲明一個簡單的 class 模板嗎?


注意:這個問題之前的措辭對於提問者實際想要完成的事情是相當含糊的,並且可能應該因為不夠清楚而被關閉。 但是,從那時起,這個問題多次被稱為規范的“字符串文字類型參數”問題。 因此,它已被重新措辭以同意該前提。

可以擁有一個const char*非類型模板參數,並通過static鏈接將const char[]變量傳遞給它,這與直接傳遞字符串文字相去甚遠。

#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();
}

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

從 Neil 的回答中進一步了解:根據需要使用帶有模板的字符串的一種方法是定義一個特征類並將字符串定義為該類型的特征。

#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();
}

版畫

My name is: Hello int
My name is: Hello

抱歉,C++ 目前不支持使用字符串文字(或真實文字)作為模板參數。

但是重新閱讀您的問題,這就是您要問的嗎? 你不能說:

foo <"bar"> x;

但你可以說

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

foo <const char *> f( "bar" );

C++20 fixed_string + "非類型模板參數中的類類型"

顯然,對此的提議首先被接受,但隨后被刪除: “字符串文字作為非類型模板參數”

刪除的部分原因是它被認為很容易處理另一個被接受的提案: “非類型模板參數中的類類型”

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

template <std::basic_fixed_string Str>
struct A {};
using hello_A = A<"hello">;

一旦我看到支持它的編譯器,我將嘗試用一個示例來更新它,該示例實際上告訴我任何事情。

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();
}

來自 Ubuntu PPA 的g++ -std=c++2a 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

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

最后,EWG 決定撤消先前批准的提案,以允許在非類型模板參數中使用字符串文字,因為允許在非類型模板參數中使用類類型的更通用的工具(剛剛批准)是一個足夠好的替代品。 (這是與上次會議相比的變化,當時看起來我們都想要。)主要區別在於您現在必須將字符數組包裝到一個結構中(想想 fixed_string 或類似的),並將其用作模板參數類型。 (P0424 的用戶自定義字面量部分仍在繼續,對允許的模板參數類型進行了相應的調整。)

if constexpr : if / else 在 C++ 編譯時使用 C++17,這將特別酷

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

這是使用 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;
}

打印:

any string as template argument

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

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();
}

嘗試將函數的地址作為模板參數傳遞。

編輯:好的,您的問題標題似乎具有誤導性

“我想要一個在其構造函數中帶有兩個參數的類。第一個可以是 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;

};

這適用於任何類型,對於第一個參數: intfloatdouble等等。

現在,如果您真的想將第一個參數的類型限制為只有intfloatdouble 你可以想出一些更精細的東西,比如

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?");
}

如果您刪除最后一行的注釋,您實際上會得到一個編譯器錯誤。


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 

—結束示例] —結束說明]

看起來它不會在即將到來的 C++0X 中改變, 請參閱當前草案 14.4.2 Template non-type arguments

根據您在 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();
}

版畫

event_A
event_B

您不能直接將字符串文字作為模板參數傳遞。

但你可以接近:

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

...
// or:
MyPrint<typestring_is("another text")>();
...

你所需要的只是一個來自這里的小頭文件。


替代品:

  • 定義一個全局char const *並將其作為指針傳遞給模板。 這里

    缺點:需要模板參數列表之外的額外代碼。 如果您需要指定字符串文字“inline”,則不適合。

  • 使用非標准語言擴展。 這里

    缺點:不保證適用於所有編譯器。

  • 使用BOOST_METAPARSE_STRING 這里

    缺點:您的代碼將依賴於 Boost 庫。

  • 使用 char 的可變參數模板參數包,例如str_t<'T','e','s','t'>

    這就是上述解決方案在幕后為您所做的。

我想要一個在其構造函數中帶有兩個參數的類。 第一個可以是 int、double 或 float,所以 ,第二個始終是字符串文字“我的字符串”

template<typename T>
class demo
{
   T data;
   std::string s;

   public:

   demo(T d,std::string x="my string"):data(d),s(x) //Your constructor
   {
   }
};

我不確定,但這是你想要的嗎?

也許不是 OP 所要求的,但是如果您使用boost ,則可以創建這樣的宏,例如:

#define C_STR(str_) boost::mpl::c_str< BOOST_METAPARSE_STRING(str_) >::value

然后使用如下:

template<const char* str>
structe testit{
};
testit<C_STR("hello")> ti;

使用代理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;
}
template <char... elements>
struct KSym /* : optional_common_base */ {
  // We really only care that we have a unique-type and thus can exploit being a `""_ksym singleton`
  const char z[sizeof...(elements) + 1] = { elements..., '\0' };
  // We can have properties, we don't need anything to be constexpr for Rs
};
template <typename T, T... chars>
auto&& operator""_ksym() { 
  static KSym<chars...> kSym; // Construct the unique singleton (lazily on demand)
  return kSym;
}
static auto ksym_example1 = "a unique string symbol1\n"_ksym.z;
static auto ksym_example2 = "a unique string symbol2\n"_ksym.z;
auto dont_care = []() {
  ::OutputDebugString(ksym_example1);
  ::OutputDebugString("a unique string symbol2\n"_ksym.z);
  assert("a unique string symbol1\n"_ksym.z == ksym_example1);
  assert("a unique string symbol2\n"_ksym.z == ksym_example2);
  return true; 
}();

以上是在 Windows 上使用Clang 11在生產中為我工作的。

(已編輯)我現在在Windows 上的clang 中完全使用這個

// P0424R1: http://www.open-std.org/jtc1/SC22/wg21/docs/papers/2017/p0424r1.pdf
template <char... chars_ta> struct KSymT;
template <typename T, T... chars_ta> // std::move(KSymT<chars_ta...>::s);
auto operator""_ksym()->KSymT<chars_ta...>& { return KSymT<chars_ta...>::s; }
struct KSym {
  virtual void onRegister() {}
  virtual std::string_view zview_get() = 0;
};

template <char... chars_ta>
struct KSymT : KSym {
  inline static KSymT s;
  // We really only care that we have a unique-type and thus can exploit being a `""_ksym singleton`
  inline static constexpr char z[sizeof...(chars_ta) + 1] = { chars_ta..., '\0' };
  inline static constexpr UIntPk n = sizeof...(chars_ta);
  // We can have properties, we don't need anything to be constexpr for Rs
  virtual std::string_view zview_get() { return std::string_view(z); };
  //#KSym-support compare with `Af_CmdArgs`
  inline bool operator==(const Af_CmdArgs& cmd) {
    return (cmd.argl[0] == n && memcmp(cmd.argv[0], z, n) == 0);
  }
};

我在一個類似的問題上苦苦掙扎,最后想出了一個簡潔的實現,將字符串文字解包為char...模板參數包,而不使用 GNU 文字運算符模板擴展:

#include <utility>

template <char ...Chars>
struct type_string_t {
    static constexpr const char data[sizeof...(Chars)] = {Chars...};
};

template <char s(std::size_t), std::size_t ...I>
auto type_string_impl(std::index_sequence<I...>) {
    return type_string_t<s(I)...>();
}

#define type_string(s) \
    decltype (type_string_impl<[] -> constexpr (std::size_t i) {return s[i];}> \
        (std::make_index_sequence<sizeof (s)>()))

static_assert (std::is_same<type_string("String_A"),
                            type_string("String_A")>::value);
static_assert (!std::is_same<type_string("String_A"),
                             type_string("String_B")>::value);

一個主要警告:這取決於 C++20 功能(類值作為非類型模板參數; P0732P1907 ),該功能(截至 2020 年 12 月)僅(部分)在 GCC 9 及更高版本中實現(預處理器功能測試: (__cpp_nontype_template_args >= 201911L) || (__GNUG__ >= 9) )。 然而,由於該功能是標准的,其他編譯器趕上只是時間問題。

另一個我沒有看到提到的 C++20 解決方案,但它足夠簡單並且適合我自己的需要,是使用 constexpr lambda 作為返回字符串的 NTTP:

#include <string_view>

template<auto getStrLambda>
struct MyType {
    static constexpr std::string_view myString{getStrLambda()};
};

int main() {
    using TypeWithString = MyType<[]{return "Hello world!";}>;
    return 0;
}

編譯器資源管理器示例在這里

字符串文字“我的字符串”,所以我猜是 const char * const

實際上,具有 n 個可見字符的字符串文字是const char[n+1]

#include <iostream>
#include <typeinfo>

template<class T>
void test(const T& t)
{
    std::cout << typeid(t).name() << std::endl;
}

int main()
{
    test("hello world"); // prints A12_c on my compiler
}

這是一個解決方案和擴展/示例

我的解決方案擴展了 https://ctrpeach.io/posts/cpp20-string-literal-template-parameters/

#include <iostream>
#include <algorithm>
#include <string>

template<size_t N>
struct StringLiteral {
    char value[N];
    constexpr StringLiteral(const char(&str)[N]) {
        std::copy_n(str, N, value);
    }
};

template <StringLiteral T>
struct String {
    static constexpr std::string str() {
        return T.value;
    }
};

template <typename... Strings>
struct JoinedString {
    static constexpr std::string str() {
        return (Strings::str() + ...);
    }
};

template <typename Delim, typename String, typename... Strings>
struct DelimJoinedString {
    static constexpr std::string str() {
        if constexpr (sizeof...(Strings))
            return JoinedString<String, Delim, DelimJoinedString<Delim, Strings...>>::str();
        else
            return String::str();
    }
};

int main() {
    // "123"
    using s123 = String<"123">;
    std::cout << s123::str() << "\n";

    // "abc"
    using abc = String<"abc">;
    std::cout << abc::str() << "\n";

    // "abc123abc123"
    using abc123abc123 = JoinedString<abc, s123, abc, s123>;
    std::cout << abc123abc123::str() << "\n";

    // "abc, 123"
    using abccomma123 = DelimJoinedString<String<", ">, abc, s123>;
    std::cout << abccomma123::str() << "\n";

    // "abc, 123, 123, abc"
    using commaabc123123abc = DelimJoinedString<String<", ">, abc, s123, s123, abc>;
    std::cout << commaabc123123abc::str() << "\n";
    return 0;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM