Plus précisément, j'ai un vecteur d'une certaine structure
typedef struct VkExtensionProperties {
char extensionName[VK_MAX_EXTENSION_NAME_SIZE];
uint32_t specVersion;
} VkExtensionProperties;
où someStructVariable.extensionName renvoie une chaîne.
Et je veux créer un ensemble de extensionName , quelque chose comme ceci std :: set .
Le processus est assez simple lorsqu'il est fait avec un pour les boucles mais je souhaite utiliser à la place std :: transform de .
std: : transform a quatre paramètres.
1,2 . Première plage (pour transformer la première plage de et en)
3 . Deuxième plage / inserteur (pour transformer la deuxième plage)
4 . Une fonction
C'est ce que j'ai jusqu'à présent
auto lambdaFn =
[](SomeStruct x) -> const char* { return x.extensionName; };
std::transform(availableExtensions.begin(),
availableExtensions.end(),
std::inserter(xs, xs.begin()),
lambdaFn);
car il n'y a pas de "contexte approprié" pour std :: back_inserter dans std :: set J'utilise std :: inserter (xs, xs.begin ()) .
Le problème est que j'essaye de renvoyer la pile mem dans ma fonction lambda. Alors, comment contourner ce problème?
Curieusement, si je supprime return de la fonction, cela fonctionne exactement comme je m'y attendais! Mais je ne comprends pas pourquoi et cela fait peur des répercussions futures.
EDIT:
J'utilise plusieurs structures à la place de SomeStruct code > comme VkExtensionProperties défini dans vulkan_core
std::vector<SomeStruct> extensions = getThoseExtensions();
3 Réponses :
Il y a deux choses que vous pouvez faire ici.
SomeStruct , il est préférable de remplacer ce membre par std :: string . const auto & obj . Cela ne créera pas de copie et ne pointera pas vers l'objet du conteneur. Cependant, j'ai toujours peur de cette solution car cela sent le mauvais design de classe où la propriété et la durée de vie des membres sont ambiguës. Vous ne pouvez probablement pas créer un ensemble de char * à moins que toutes les instances de extensionName avec la même valeur pointent vers le même tableau de caractères (cela stockerait des pointeurs uniques à la place de valeurs uniques). Si vous utilisez à la place std::set<:string> , cela fonctionnera à la fois et ne stockera que des valeurs uniques et résoudra votre problème de durée de vie variable comme std :: string prend soin de se copier (ou déplacer) pour vous si nécessaire:
auto lambdaFn =
[](const SomeStruct& x) { return std::string(x.extensionName); };
std::set<std::string> xs;
std::transform(availableExtensions.begin(),
availableExtensions.end(),
std::inserter(xs, xs.begin()),
lambdaFn);
Une façon de faire ce que vous voulez est d'utiliser le lambda suivant
const char* str = "Hello World!";
Le problème avec ceci est que vous enregistrez des pointeurs vers la mémoire appartenant à quelqu'un d'autre (ces chaînes). Lorsqu'ils sont détruits (cela semble être le cas à la fin de la fonction), le pointeur sera suspendu. Vous pouvez contourner cela en allouant de la mémoire dans votre lambda et en copiant les données:
auto lambda = [](const SomeStruct & x) -> const char* {
char* c = new char[x.extensions.length()+1];
std::strcpy(c, x.extensions.data());
return c;
}
Mais alors vous devez faire la gestion de la mémoire vous-même (c'est-à-dire n'oubliez pas de libérer ces const char * ). Et c'est une mauvaise idée. Vous devriez probablement reconsidérer ce que vous faites. Pourquoi utilisez-vous const char * ici et non std :: string ?
N'oubliez pas que l'utilisation typique de const char * est de sauvegarder les littéraux de chaîne i C-code, c'est-à-dire le code
auto lambda = [](const SomeStruct& x) -> const char* { return x.extensions.data();};
crée un tableau char de taille suffisante dans la section statique de la mémoire, l'initialise avec la chaîne (une constante de temps de compilation) puis enregistre un pointeur vers celui dans str code >. C'est aussi pourquoi cela doit être un const char * car un autre pointeur faisant référence à un littéral de chaîne égal peut (ou non) pointer exactement le même tableau de caractères et vous ne voulez pas activer le changement là. Alors n'utilisez pas simplement const char * car vous voyez des chaînes en C enregistrées dans ces const char * sans que personne n'ait besoin de les libérer plus tard.
p >
Quel " stack mem " renvoyez-vous? Je suppose que
SomeStructne possède pas la chaîne de caractères pointée parextensionName, non? Que signifie " si je supprimereturn"? Si vous supprimez le mot-cléreturn, votre lambda aura un comportement indéfini car il ne retournera pas de valeur lorsqu'il prétend retourner non-void.veuillez fournir un exemple reproductible minimal . Btw
std :: seta un constructeur qui prend deux itérateurs vers une plage d'éléments à insérer. Vous n'avez pas besoin destd :: transformpas le problème: le nom
functionPointerest un peu trompeur. Il n'y a pas de pointeur de fonction dans votre code.functionPointerest un lambda@ formerlyknownas_463035818 Encore aurait besoin d'un itérateur de transformation si OP veut extraire le membre de chaque élément.
Votre
SomeStructest passé par valeur àfunctionPointer, ce qui pourrait potentiellement entraîner des problèmes. Devrait voir la définition deSomeStruct.@walnut oh oui, ma faute. J'ai manqué ça
@super J'utilise plusieurs structures différentes à la place de
SomeStruct, l'une est VkExtensionProperties@walnut J'ai supposé qu'à cause de cet avertissement sonore, j'en recevais "l'adresse de la mémoire de la pile associée à la variable locale renvoyée". Par remove
return, je voulais en fait supprimer le mot-cléreturnde la fonction lambda.@atis Votre question dit que
extensionNameest une chaîne et votre lien dit que c'est unchar *, et vous dites également que vous utilisez une structure différente. Alors s'il vous plaît clarifiez. Le type deextensionNameest-il toujourschar *ou pourrait-il être potentiellement des choses différentes commestd :: stringetc.?@atis La suppression du mot-clé
returnentraîne un comportement indéfini et il aurait dû y avoir également un avertissement par clang. L'avertissement donne l'impression queSomeStructpossède la mémoire vers laquelle pointeextensionName. Dans ce cas, votre approche de prendre (non-propriétaire)const char *comme type d'élément dusetest probablement déjà en elle-même cassée (en particulier si vous ne faites pas sûr que lestd :: vectorsurvit austd :: set). Voir par exemple réponse d'AlanBirtles pour avoir corrigé ce problème.@atis D'autres ont mentionné la durée de vie du vecteur - mais même si le vecteur survit à l'ensemble, sachez que l'ajout d'éléments à un vecteur est autorisé à déplacer le vecteur ailleurs en mémoire, ce qui invaliderait tout i > les
char *dans l'ensemble! Juste une autre raisonstd::set est une bonne idée.