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
SomeStruct
ne 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 :: set
a un constructeur qui prend deux itérateurs vers une plage d'éléments à insérer. Vous n'avez pas besoin destd :: transform
pas le problème: le nom
functionPointer
est un peu trompeur. Il n'y a pas de pointeur de fonction dans votre code.functionPointer
est un lambda@ formerlyknownas_463035818 Encore aurait besoin d'un itérateur de transformation si OP veut extraire le membre de chaque élément.
Votre
SomeStruct
est 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éreturn
de la fonction lambda.@atis Votre question dit que
extensionName
est 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 deextensionName
est-il toujourschar *
ou pourrait-il être potentiellement des choses différentes commestd :: string
etc.?@atis La suppression du mot-clé
return
entraîne un comportement indéfini et il aurait dû y avoir également un avertissement par clang. L'avertissement donne l'impression queSomeStruct
possède la mémoire vers laquelle pointeextensionName
. Dans ce cas, votre approche de prendre (non-propriétaire)const char *
comme type d'élément duset
est probablement déjà en elle-même cassée (en particulier si vous ne faites pas sûr que lestd :: vector
survit 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.