Domanda

E 'possibile creare un costruttore (o la firma di funzione, in questo caso) che solo accetta una stringa letterale, ma non un esempio char const *?

E 'possibile avere due overload in grado di distinguere tra le stringhe letterali e char const *?

C ++ 0x sarebbe sorta-di consentire a questo con un suffisso personalizzato -. Ma sto cercando una soluzione "in precedenza"

Motivazione: per evitare la copia mucchio di stringhe che non verrà modificato quando somministrato come stringhe letterali.

Queste stringhe direttamente andare a un'API in attesa di un const char * senza alcuna elaborazione. La maggior parte delle chiamate utilizzati letterali che non richiedono ulteriore elaborazione, solo in pochi casi sono costruiti. Sto cercando una possibilità per preservare il comportamento chiamata nativa.

Nota: - dal momento che viene in su nelle risposte: il codice in questione non usa std::string a tutti, ma un buon esempio potrebbe essere:

class foo
{
   std::string m_str;
   char const * m_cstr;      
 public:
   foo(<string literal> s) : m_cstr(p) {}
   foo(char const * s) : m_str(s) { m_cstr = s.c_str(); }
   foo(std::string const & s) : m_str(s) { m_cstr = s.c_str(); }

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

Risultati:

(1) che non può essere fatto.
(2) ho capito che non sono nemmeno in cerca di un letterale, ma per una ( "tutto ciò che non ha bisogno di essere copiato" cioè) in fase di compilazione costante.

io probabilmente utilizzare il seguente motivo invece:

const literal str_Ophelia = "Ophelia";

void Foo()
{
  Hamlet(str_Ophelia, ...);  // can receive literal or string or const char *
}

con un semplice

struct literal  
{ 
   char const * data; 
   literal(char const * p) : data(p) {} 
   operator const char *() const { return data; }
};

Ciò non impedisce a nessuno di abusarne (dovrei trovare un nome migliore ...), ma consente l'ottimizzazione richiesto, ma rimane sicuro per impostazione predefinita.

È stato utile?

Soluzione

No, semplicemente non è possibile fare questo - stringhe letterali e const char * sono intercambiabili. Una soluzione potrebbe essere quella di introdurre una classe speciale per contenere puntatori a stringhe e fare un costruttore solo accettare che. In questo modo ogni volta che si ha bisogno di passare un letterale si chiama un costruttore di quella classe e passare l'oggetto temporaneo. Ciò non impedisce del tutto l'uso improprio, ma rende il codice molto più gestibile.

Altri suggerimenti

Soluzione di lavoro sulla base di SBI idea :

struct char_wrapper
{
    char_wrapper(const char* val) : val(val) {};
    const char* val;
};

class MyClass {
public:
  template< std::size_t N >
  explicit MyClass(const char (&str)[N])
  {
      cout << "LITERAL" << endl;
  }
  template< std::size_t N >
  explicit MyClass(char (&str)[N])
  {
      cout << "pointer" << endl;
  }    
  MyClass(char_wrapper m)
  {
     cout << "pointer" << endl;
  }
};

int main()
{
    MyClass z("TEST1");     // LITERAL
    const char* b = "fff";
    MyClass a(b);           // pointer
    char tmp[256]; 
    strcpy(tmp, "hello"); 
    MyClass c(tmp);         // pointer
}

Si, può essere fatto! Mi si avvicinò con una soluzione che funziona con C ++ 03 e senza una classe wrapper (che rompe alcune conversioni implicite nelle dichiarazioni di ritorno).

Prima di tutto, è necessario un modello di costruzione per il tipo const char (&)[N], dal momento che questo è il tipo originale di stringhe letterali. Allora hai bisogno anche di un altro per il tipo char (&)[N] - come buffer char, per esempio - in modo che cont finire nel costruttore per letterali. E probabilmente si vuole anche un costruttore normale per il tipo di const char*.

template<int N> Foo(const char (&)[N]); // for string literals
template<int N> Foo(char (&)[N]);       // for non-const char arrays like buffers
Foo(const char*);                       // normal c strings

Il problema ora è che per stringhe letterali il compilatore pensa ancora, che il costruttore const char* è una scelta migliore di un'istanza di modello, in quanto array-to-pointer conversioni sono exact- partita rango . (13.3.3.1.1)

Quindi, il trucco è quello di abbassare la precedenza del costruttore const char*. Questo può essere fatto cambiando ad un modello come bene e usando SFINAE di corrispondere solo contro il tipo const char*. Il costruttore non ha alcun valore di ritorno e un solo parametro, che è necessaria per il tipo di deduzione. Quindi un'altra "parametro fittizio" con il valore di default è necessario, che utilizza il tipo caratteristica condizionale: template<typename T> Foo(T, typename IsCharPtr<T>::Type=0)

Soluzione:

#include <iostream>

#define BARK std::cout << __PRETTY_FUNCTION__ << std::endl

struct Dummy {};
template<typename T> struct IsCharPtr {};
template<> struct IsCharPtr<const char *> { typedef Dummy* Type; };
template<> struct IsCharPtr<char *> { typedef Dummy* Type; };

struct Foo {
  template<int N> Foo(const char (&)[N]) { BARK; }
  template<int N> Foo(char (&)[N]) { BARK; }
  template<typename T> Foo(T, typename IsCharPtr<T>::Type=0) { BARK; }
};

const char a[] = "x";
const char* b = "x";
const char* f() { return b; }

int main() {
  char buffer[10] = "lkj";
  char* c = buffer;
  Foo l("x");     // Foo::Foo(const char (&)[N]) [N = 2]
  Foo aa(a);      // Foo::Foo(const char (&)[N]) [N = 2]
  Foo bb(b);      // Foo::Foo(T, typename IsCharPtr<T>::Type) [T = const char *]
  Foo cc(c);      // Foo::Foo(T, typename IsCharPtr<T>::Type) [T = char *]
  Foo ee(buffer); // Foo::Foo(char (&)[N]) [N = 10]
  Foo ff(f());    // Foo::Foo(T, typename IsCharPtr<T>::Type) [T = const char *]
  return 0;
}

Se si sa esattamente come il vostro compilatore e trattare la piattaforma con le stringhe, potrebbe essere possibile scrivere una soluzione che può fare questo. Se sapete che il vostro compilatore mette sempre stringhe letterali in una regione specifica della memoria, è possibile controllare il puntatore contro i limiti di quella memoria. Se cade all'interno di quel blocco, hai una stringa letterale; altrimenti hai una stringa memorizzata sul mucchio o una pila.

Tuttavia, questa soluzione sarebbe la piattaforma / compilatore-specifica. Non sarebbe portatile.

Su alcune piattaforme, ho dovuto dichiarare stringhe letterali come static const char * in modo che il programma per accedere al testo da Read-Only Memory. Quando dichiarato const char *, l'elenco assemblea ha mostrato che il testo è stato copiato da ROM su una variabile di stack.

Invece di preoccuparsi del ricevitore, magari provare dichiarare le stringhe letterali con static const char *.

Con un nuovo letterali definiti dall'utente in C ++ 14 (come per Clang 3.5 - funziona con C ++ 11 troppo), non v'è una soluzione elegante:

class Literal {
 public:
  explicit Literal(const char* literal) : literal_(literal) {}
  // The constructor is public to allow explicit conversion of external string
  // literals to `_L` literals. If there is no such need, then move constructor
  // to private section.

  operator const char* () { return literal_; }

 private:
  friend Literal operator"" _L (const char*, unsigned long);
  // Helps, when constructor is moved to private section.

  const char* literal_;
};

Literal operator"" _L (const char* str, unsigned long) {
  return Literal(str);
}

Può essere utilizzato in questo modo:

void f1(Literal) {}  // Accepts literals only.

int main() {
  auto str1 = "OMG! Teh Rey!"_L;
  std::cout << str1 << std::endl;
  f(str1);
}

Non è uno svantaggio: si deve accodare _L ad ogni letterale - ma non è un grosso problema, in realtà

.
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top