2013-08-03 10 views
7

Ich versuche, eine bequeme Möglichkeit zu finden, Zeichenfolgenliterale als Vorlageargumente zu übergeben. Ich sorge mich nicht darum, die größtmögliche Anzahl von Compilern zu unterstützen, ich verwende die neueste Version von g ++ mit --std=c++0x.Versuchen, String-Literale als Vorlageargumente zu übergeben

Ich habe viele mögliche Lösungen ausprobiert, aber alle haben mich enttäuscht. Ich gebe auf, aber zuerst möchte ich wissen, warum ein paar von ihnen fehlgeschlagen.

Hier sind sie:

#include <iostream> 
#include <string> 

using namespace std; 

struct String { 
    char const *m_sz; 

    constexpr String(char const *a_sz) 
     : 
    m_sz(a_sz) {} 

    char const *operator()() const { 
     return m_sz; 
    } 
}; 

template<class _rstr> 
string const Get() { 
    return _rstr(); 
} 

int main() { 
    cout << Get<String("hello")>() << endl; 
    return 0; 
} 

Und:

#include <iostream> 
#include <string> 

using namespace std; 

struct String { 
    char const *m_sz; 

    constexpr String(char const *a_sz) 
     : 
    m_sz(a_sz) {} 
}; 

template<String const &_rstr> 
string const Get() { 
    return _rstr.m_sz; 
} 

int main() { 
    String constexpr str = "hello"; 
    cout << Get<str>() << endl; 
    return 0; 
} 

Das Ziel war eine bequeme Art und Weise zu finden, ein Stringliteral an die nutzlose Get-Funktion übergeben, die als seine Vorlage Argument zurückgibt std :: string-Objekt.

EDIT: Entschuldigung, vielleicht ist meine Hauptfrage nicht klar. Meine Frage ist: Warum scheitern diese beiden Schnipsel?

+1

Im ersten Fall 'String ("Hallo")' ist ein Wert, kein Typ, daher kann er nicht an die Vorlage 'Get <>' übergeben werden, die einen Typ erwartet. Im zweiten Fall erlaubt C++ 11 keine willkürlichen benutzerdefinierten Typen (wie "String") als Vorlagenparameter. –

Antwort

3

re: Ihre OP: I'd like to know why a couple of them failed.

Der Kommentar von @NatanReed ist richtig:

  • Ihr erster Schnipsel schlägt fehl, da Get ein TYPE benötigt und ist ein object gegeben.
  • Ihr zweites Snippet schlägt fehl, weil es illegal ist, ein Template-Argument als Referenz auf ein Objekt zu definieren.
    • bis C++ 2003, das ist. Dann wurde reference to an object legal.

Template Argumente müssen Konstanten aus einer begrenzten Anzahl von Typen sein.

  • Siehe auch: ISO/IEC 14882-2003 §14.1: Template-Parameter
  • Siehe auch: ISO/IEC 14882-2003 §14.3.2: Template nicht-Typ Argumente

Und selbst dann, Die String constexpr str = "hello"; muss externe Verknüpfung haben. Es wird also nicht funktionieren, es auf den Stack innerhalb von main() zu setzen.

gibt diesen einen Versuch:

#include <iostream> 
#include <string> 

using namespace std; 

struct String { 
    char const *m_sz; 

    constexpr String(char const *a_sz) 
     : 
    m_sz(a_sz) {} 
}; 

template<String const &_rstr> 
string const Get() { 
    return _rstr.m_sz; 
} 

extern String constexpr globally_visible_str = "hello"; 
int main() { 
    cout << Get<globally_visible_str>() << endl; 
    return 0; 
} 
13

Sie können nicht Stringliterale als Template-Argument für die einfachen Grund, dass es nicht spezifiziert, ob zwei Instanzen eines wörtlichen mit dem gleichen Text sind das gleiche Objekt oder nicht verwenden. In anderen Worten gegeben:

template <char const* str> 
class TC {}; 

TC< "xyz" > v1; 
TC< "xyz" > v2; 

Es wäre nicht spezifiziert, ob v1 und v2 die gleiche Art hatten oder nicht.

Sie char const[] Variablen als Template-Argumente verwenden können, jedoch, da sie eine definierte Adresse haben:

template <char const* str> 
class TC {}; 

extern char const xyz[] = "xyz"; 
TC<xyz> v1; 
TC<xyz> v2; 

In diesem Fall v1 und v2 garantieren den gleichen Typen haben.

EDIT:

Ich denke, C++ 11 die Notwendigkeit der extern auf der Definition des Strings entfernt, zumindest, wenn die Zeichenfolge und die Instanziierung alle in derselben Übersetzungseinheit sind. Ich bin nicht sicher, aber; das einzige Mal, dass ich etwas so gemacht habe, habe ich nicht Zugriff auf C++ 11.

+0

Ich kann bestätigen, dass die Beschränkung von 'extern' entfernt wurde, weil ich es ohne es versucht habe und es funktioniert (es war einer meiner mehreren Versuche). Außerdem weiß ich, dass ich String-Literale nicht direkt übergeben kann, aber ich versuchte, dies irgendwie zu umgehen, und wahrscheinlich gescheitert. –

+0

Klarstellung: das Deklarieren einer Variablen mit externer Verknüpfung (normalerweise eine globale) ist für mich nicht "komfortabel".In der Lage zu sein, eine Art "String" -Adapter zu verwenden, wie in meinen zwei Schnipsel, ist viel bequemer. –

5

Sie können "simulieren" Strings mit C++ 11 variadische Vorlagen:

Diese Drucke:

Hallo Welt !!!

+0

Ja, ich habe es versucht und es funktioniert, aber es ist auch nicht komfortabel. Wie auch immer, meine Hauptfrage ist: Warum funktionieren die zwei Snippets, die ich gepostet habe, nicht? –

5

Ich weiß, dass die Post ist alt, aber ich habe keine Lösung für dieses Problem hier, und vielleicht würde jemand in meiner Abhilfe interessieren:

template <int N> 
constexpr int string_literal_length(const char (&str)[N]) { 
    return N - 1; 
} 

template <int PassedLength, int CountedLength, char... Characters> 
struct string_literal { 
    static_assert(PassedLength == CountedLength, "Passed to STRING_LITERAL length does not match the length of string..."); 
}; 

#define STRING_LITERAL(N, str) string_literal<N, string_literal_length(str), STRING_LITERAL_##N(str)> 

// ... as long as we need it ... 
#define STRING_LITERAL_128(str) STRING_LITERAL_127(str), str[127] 
#define STRING_LITERAL_127(str) STRING_LITERAL_126(str), str[126] 
#define STRING_LITERAL_126(str) STRING_LITERAL_125(str), str[125] 
#define STRING_LITERAL_125(str) STRING_LITERAL_124(str), str[124] 
// ... 
#define STRING_LITERAL_5(str) STRING_LITERAL_4(str), str[4] 
#define STRING_LITERAL_4(str) STRING_LITERAL_3(str), str[3] 
#define STRING_LITERAL_3(str) STRING_LITERAL_2(str), str[2] 
#define STRING_LITERAL_2(str) STRING_LITERAL_1(str), str[1] 
#define STRING_LITERAL_1(str) str[0] 

Jetzt Nutzung:

template <class SLiteral> 
struct usage_of_string_literal { 
}; 

int main() { 
    usage_of_string_literal<STRING_LITERAL(12, "123456789012")> uosl; 
} 

es Arbeit ein zu schaffen, haben die Länge der Zeichenfolge zu erhalten, aber es ist leider s noch komfortablere Lösung als einfache variadische arg Vorlage von Zeichen und die Länge der static_assert prüft wird so die Compiler entsprechenden Wert zu holen ...


bearbeiten

Eine weitere Vorlage können helfen Zauber. Dieses wird unter Verwendung eines Kurzschlusses der Stringgröße von string_literal Erklärung loszuwerden (C++ 17):

#include <type_traits> 
#include <utility> 

#define MAX_STRING_LITERAL_LENGTH 11 
#define STRING_LITERAL(str) string_literal<char_pack<STRING_LITERAL_11(str)>>::s 

#define STRING_LITERAL_11(str) STRING_LITERAL_10(str), ((TERMINATED_10(str))?(str[10]):('\0')) 
#define STRING_LITERAL_10(str) STRING_LITERAL_9(str), ((TERMINATED_9(str))?(str[9]):('\0')) 
#define STRING_LITERAL_9(str) STRING_LITERAL_8(str), ((TERMINATED_8(str))?(str[8]):('\0')) 
#define STRING_LITERAL_8(str) STRING_LITERAL_7(str), ((TERMINATED_7(str))?(str[7]):('\0')) 
#define STRING_LITERAL_7(str) STRING_LITERAL_6(str), ((TERMINATED_6(str))?(str[6]):('\0')) 
#define STRING_LITERAL_6(str) STRING_LITERAL_5(str), ((TERMINATED_5(str))?(str[5]):('\0')) 
#define STRING_LITERAL_5(str) STRING_LITERAL_4(str), ((TERMINATED_4(str))?(str[4]):('\0')) 
#define STRING_LITERAL_4(str) STRING_LITERAL_3(str), ((TERMINATED_3(str))?(str[3]):('\0')) 
#define STRING_LITERAL_3(str) STRING_LITERAL_2(str), ((TERMINATED_2(str))?(str[2]):('\0')) 
#define STRING_LITERAL_2(str) STRING_LITERAL_1(str), ((TERMINATED_1(str))?(str[1]):('\0')) 
#define STRING_LITERAL_1(str) str[0] 


#define TERMINATED_10(str) TERMINATED_9(str) && str[9] 
#define TERMINATED_9(str) TERMINATED_8(str) && str[8] 
#define TERMINATED_8(str) TERMINATED_7(str) && str[7] 
#define TERMINATED_7(str) TERMINATED_6(str) && str[6] 
#define TERMINATED_6(str) TERMINATED_5(str) && str[5] 
#define TERMINATED_5(str) TERMINATED_4(str) && str[4] 
#define TERMINATED_4(str) TERMINATED_3(str) && str[3] 
#define TERMINATED_3(str) TERMINATED_2(str) && str[2] 
#define TERMINATED_2(str) TERMINATED_1(str) && str[1] 
#define TERMINATED_1(str) str[0] 

template <char... Cs> 
struct char_pack { 
    static constexpr char const arr[sizeof...(Cs) + 1] = {Cs..., 0}; 
    static constexpr std::size_t non_zero_count = (((Cs != 0)?1:0) + ...); 
    static_assert(non_zero_count < MAX_STRING_LITERAL_LENGTH, "You need to create more macros"); 
}; 

template <char... Cs> 
constexpr char const char_pack<Cs...>::arr[sizeof...(Cs) + 1]; 

template <char... Cs> 
constexpr std::size_t char_pack<Cs...>::non_zero_count; 

template <class CP, class = void, class = std::make_index_sequence<CP::non_zero_count>> 
struct string_literal; 

template <char... Cs, std::size_t... Is> 
struct string_literal<char_pack<Cs...>, std::enable_if_t<(Cs && ...)>, std::index_sequence<Is...>> { 
    static constexpr char const s[sizeof...(Cs) + 1] = {Cs..., '\0'}; 
}; 

template <char... Cs, std::size_t... Is> 
constexpr char const string_literal<char_pack<Cs...>, std::enable_if_t<(Cs && ...)>, std::index_sequence<Is...>>::s[sizeof...(Cs) + 1]; 

template <char... Cs, std::size_t... Is> 
struct string_literal<char_pack<Cs...>, std::enable_if_t<!(Cs && ...)>, std::index_sequence<Is...>>: string_literal<char_pack<char_pack<Cs...>::arr[Is]...>> { }; 

template <const char *> 
struct foo {}; 

int main() { 
    foo<STRING_LITERAL("abcdefghij")> f; 
    static_cast<void>(f); 
} 

[live demo]

+0

Ich denke, dass die Weitergabe der Länge trivialerweise durch ein weiteres Indirektions-Makro entfernt werden kann. '#define STRING_LITERAL_ (STR) STRING_LITERAL (sizeof (STR) - 1, STR)'. Verwendung: 'STRING_LITERAL _ (" 123456789012 ")'. BTW, es gibt auch eine Boost-Einrichtung, wie in [diese Antwort] erwähnt (http://stackoverflow.com/a/18154638/514235). Allerdings ist Ihre Antwort gut, dass es Readymade-Lösung bietet. Sie können auch ein "const char" -Array in die "class string_literal" einfügen, das einfach alle einzelnen Zeichen erneut in eine Zeichenfolge speichert. – iammilind

+1

Bestätigte Ihren Code & es scheint, dass alles, was Sie getan haben, korrekt ist; d. h., wir müssen die Länge explizit als eine Literalnummer übergeben, damit dieser Code funktioniert. Ich hatte den Eindruck, dass Sie implementiert haben, wie Boost's 'MPLLIBS_STRING' implementiert wird. Es scheint, dass es einige komplexe Tricks geben muss, damit dieses Makro funktioniert. – iammilind

+0

@iammilind Ich habe von 'BOOST_HANA_STRING' gehört, aber soweit ich weiß, kann es nicht im unbewerteten Kontext verwendet werden, da es lambdas verwendet ... Ich werde den' MPLLIBS_STRING' nachschlagen. Was die Länge anbelangt, kann sie nicht mit 'sizeof' ausgetauscht werden, da ich die Verkettung für eine einfache erweiterbare Schleife verwendet habe. Vielleicht haben sie hier einen komplexeren Preprozessor-Loop-Mechanismus benutzt ... –