Perché ricevo errori complessi anche con un semplice codice C ++?
Ho iniziato a giocare con C ++ di recente. Una delle difficoltà che ho abbastanza spesso è quando il compilatore dice che c'è un problema con i tipi: il più delle volte, quegli errori del compilatore mi sembrano estremamente criptici. Ecco un esempio:
Smooth.h: 63: 15: errore: conversione definita dall'utente non valida da "Smooth <T, S> :: sum (std :: size_t) [con T = short unsigned int; int lungo senza segno S = 20; std :: size_t = long unsigned int] :: <lambda (short unsigned int, short unsigned int)> 'to' short unsigned int (*) (short unsigned int, short unsigned int)
Quando lavoravo con C #, ricordo che c'era un modo per ottenere errori piuttosto complicati combinando i generici, cioè usare i generici come parametri di altri generici, possibilmente a più di due livelli, ma uno doveva scrivere codice WTF per quello, come :
Action<IEnumerable<Tuple<int?, Func<int?, ICollection<int>, bool>>>
In C ++, tuttavia, ho quegli errori criptici con il codice che considererei piuttosto semplice. Allora cosa sta succedendo?
- È un'abilità per capire gli errori dati dal compilatore?
- Oppure non dovrei ottenere quegli errori complicati in primo luogo e la loro presenza indica che sto facendo qualcosa di sbagliato?
Come richiesto nei commenti, ecco il codice sorgente corrispondente, almeno la parte rilevante. Contesto: il codice è progettato per funzionare su un microcontrollore, quindi STL non è disponibile, il che rende necessario reinventare la ruota.
template <typename T, size_t S>
class Smooth {
T sum(size_t of = S) {
return this->reduce(
T(),
[](T acc, T value) { return acc + value; },
0,
of);
}
T reduce(
const T initial,
T (*acc)(T, T),
const size_t skip = 0,
const size_t take = S) {
...
}
};
L'errore effettivo era all'interno return acc + value;e succede se Tnon è un numero intero, ma, per esempio, uint16_t. In questo caso, il risultato lambda è un numero intero, mentre Tè previsto. La correzione consiste nella sostituzione di lambda con return static_cast<T>(acc + value);.
Risposte
È un'abilità per capire gli errori dati dal compilatore?
In generale, sì. Ci sono molte informazioni racchiuse in esso, che puoi estrarre in qualcosa di più leggibile con la pratica.
-
Smooth.h: 63: 15: errore:
Hai un errore nella riga 63, colonna 15. Penso che questo sia solo l'inizio di lambda, ma qualsiasi editor o IDE decente te lo mostrerà.
-
conversione definita dall'utente non valida da
Sta cercando di convertire qualcosa. Non abbiamo conversioni ovvie nel codice, quindi questa è probabilmente la conversione da lambda senza stato a puntatore a funzione
-
Smooth<T, S>::sum(std::size_t) [with T = short unsigned int; long unsigned int S = 20; std::size_t = long unsigned int]::<lambda(short unsigned int, short unsigned int)>Questa è la parte difficile da leggere IMO. Poiché il tipo lambda è locale rispetto alla funzione, lo chiama:
Smooth<T, S>::sum(std::size_t)::<lambda(short unsigned int, short unsigned int)>ma questo dipende dai parametri del modello esterno, quindi inserisce quelli al centro:
[with T = short unsigned int; long unsigned int S = 20; std::size_t = long unsigned int]il che rende molto più difficile la lettura. Mettere solo questo bit su una riga separata aiuterebbe, e altri compilatori lo fanno. Per confronto, l'errore di GCC (sul trunk, al momento della scrittura: link ) è davvero dettagliato, ma ben formattato su più righe
-
per
short unsigned int (*)(short unsigned int, short unsigned int)che è il tipo del secondo parametro per
reduce.
Stai facendo qualcosa di un po 'complesso però: mescolare puntatori a funzione e lambda è l'unica ragione per cui è richiesta la conversione.
Penso che in questo contesto sarebbe più normale reduceessere modellato su un tipo funtore, quindi potrebbe prendere un lambda senza conversione, o un puntatore a funzione o un file std::function. In questo modo potresti ricevere un avviso o un errore all'interno di reduce, se Te il tipo restituito dal funtore non sono compatibili, ma si tratterebbe di quei tipi specifici.
Ad esempio, se modifichi il tuo metodo std::reduce, sembrerebbe
template <class BinaryOp>
T reduce(
const T initial,
BinaryOp acc,
const size_t skip = 0,
const size_t take = S) {
// ...
}
(So che hai detto che non puoi usare la libreria standard, ma puoi comunque vedere come affronta problemi simili).
La causa principale di questo tipo di messaggi di errore è che i modelli C ++ vengono tutti applicati prima della compilazione regolare, mentre la maggior parte degli altri linguaggi che lo supportano ha un polimorfismo parametrico integrato nel sistema di tipi principale. Quindi, sfortunatamente, gli errori che oltrepassano quel confine possono essere famigeratamente criptici. Tuttavia stanno migliorando. In altri 35 anni potrebbe essere effettivamente leggibile.
Si lamenta perché non può eseguire il cast di un oggetto Lambda a un puntatore a una funzione.
Devi catturare la funzione come un tipo normale (diciamo X) quindi passarla a un'altra meta funzione per decidere se è richiamabile.
template <typename X, typename enable_if = void>
struct CallAble
{};
template <typename X>
struct CallAble
< X
, std::void_t<decltype(std::declval<X>()(std::declval<T>(), std::declval<T>())>
>
{
typedef decltype(std::declval<X>()(std::declval<T>(), std::declval<T>()) type;
};
Restituirà un tipo solo se è richiamabile e quel tipo è il tipo di risultato.
Dovrai modellarlo per il tuo lavoro specifico.
Ho capito l'errore leggendo:
Smooth.h: 63: 15: errore: conversione definita dall'utente non valida da "..." a "..."
Abbastanza chiaro in realtà. Qualcosa non ha trasmesso. Cosa sta cercando di ottenere?
short unsigned int (*) (short unsigned int, short unsigned int)
(*) è la parte importante. Puntatore a una funzione.
Smooth <T, S> :: sum (std :: size_t) [con T = short unsigned int; int lungo senza segno S = 20; std :: size_t = long unsigned int] :: <lambda (short unsigned int, short unsigned int)>
va bene brutto, qual è il percorso effettivo però
Smooth <T, S> :: sum (std :: size_t) :: <lambda (short unsigned int, short unsigned int)>
Proprio così un Lambda nello spazio dei nomi / funzione Smooth <T, S> :: sum (std :: size_t).
Sì, lambda non esegue il cast ai puntatori a funzione.
Aiuta ad avere un editor di testo per ritagliare le informazioni sull'errore.