quand vous écrivez ceci
R"rawliteral(Ceci est le début : )rawliteral"
vous avez déclaré une c-string constante et sur ESP32 ce sera mis en flash (sur un Uno ce serait en RAM). C'est comme si vous aviez écrit
const char * debut = "Ceci est le début : ";
juste que le rawliteral permet de ne pas s'ennuyer avec l'échappement des caractères et plusieurs lignes.
Ce que je voulais dire c'est qu'il faut faire attention à la déclaration
si on fait
char debut[] = "Ceci est le début : ";
alors vous avez une version en flash et une version en RAM.
la première allocation est pour String(valeur), mais sinon oui vous avez compris
Avec la classe String vous pouvez optimiser un peu les choses en faisant +=
au lieu de return a + b + c; ou a, b et c sont des Strings (ce qui conduit à toutes les allocation dont on a parlé) il vaut mieux écrire
String r = a;
r += b;
r += c;
return r;
Si on écrit r = a + b + c; Selon la norme, les temporaires intermédiaires qui ne sont pas stockés dans une variable nommée sont détruits à la fin de l'instruction complète (full-expression). Donc ici ce serait après l'affectation dans r et avant de passer à l'instruction suivante.
Dans votre cas puisque c'est la dernière instruction (return) de la fonction, ça veut dire juste avant de retourner au code appelant.
Ensuite on peut avoir des optimisation, si la classe intermédiaire a implémenté ce qu'on appelle move semantics , le nombre de constructions et destructions effectives peut être réduit : ça permet de transférer les ressources d'un objet temporaire au lieu de les copier, évitant ainsi des allocations inutiles. Cela se fait avec le move constructor (T(T&&)) et le move assignment operator (T& operator=(T&&)) ➜ avec cela un objet peut "voler" les ressources d'un autre en transférant ses pointeurs plutôt qu'en dupliquant ses données. Après un move, l'objet source est laissé dans un état valide mais indéfini.