2017-07-27 1 views
4

Cela peut être un peu spécifique à l'implémentation, mais certains d'entre eux semblent fondamentaux.Deleter personnalisé efficace en mémoire pour std :: unique_ptr?

Je suis certain que je dois manquer quelque chose dans la bibliothèque standard.

Le problème est le suivant:

Je veux mettre en œuvre un std::unique_ptr dont Deleter est free()

[parce que la valeur est attribuée par malloc()]

Il y a, bien sûr, beaucoup d'options sur comment faire cela, mais (au moins en g ++ 4.8.4 pour x86-64), ils semblent avoir des implications différentes d'utilisation de la mémoire.

Exemples: Méthode 1:

std::unique_ptr<char, std::function<void(void*)>> ptr_a(malloc(10), free); 

Cependant, sizeof(ptr_a) == 40 octets (8 pour void *, 32 pour std :: fonction <>)

Méthode 2:

std::unique_ptr<void, void (*)(void*)> ptr_b(malloc(10), free); 

Un peu mieux, comme sizeof(ptr_b) == 16 octets (8 pour void *, 8 pour fonct nu pointeur ion])

Méthode 3:

template <void (*T)(void*)> 
class Caller { 
public: 
    void operator()(void* arg) { 
    return T(arg); 
    } 
}; 
std::unique_ptr<void, Caller<free>> ptr_c(malloc(10));` 

A ce stade, sizeof(ptr_c) == 8 octets (minimum possible) - mais j'ai dû introduire une classe qui est à peu près pur passe-partout (et, comme montré, facilement modélisé).

Cela ressemble à un modèle si simple - y a-t-il un élément dans la STL qui fait ce que fait Caller<> ci-dessus? Bien sûr, g ++ par défaut apparaît bien free() lors de l'appel de delete sur un type trivial - mais cela semble loin d'être garanti par le standard (si rien d'autre, new/delete peut être redéfini à partir de l'allocation par défaut/les fonctions de deallocation, et default_delete appellera alors la suppression de remplacement).

De plus, il existe d'autres cas où la libération de certains objets alloués dans une bibliothèque C pure serait implémentée par un simple appel de fonction plutôt que par un suppresseur. Il semble fastidieux de devoir utiliser ces fonctions d'allocation/désallocation dans les classes pour que std :: unique_ptr les appelle correctement et efficacement, ce qui me laisse penser qu'il me manque quelque chose (la plupart des autres spécifications C++ modernes semble être extrêmement bien pensé).

Répondre

0

Il y a aussi la possibilité d'utiliser 4 un lambda sans état:

auto free_lmbd = [](void *_ptr) { free (_ptr);}; 
std::unique_ptr<void, decltype (free_lmbd)> ptr {malloc(10), free_lmbd}; 

qui aura également 8 octets (au moins sur mon ordinateur), même que votre 3ème option.

Je recommande la lecture http://www.bfilipek.com/2016/04/custom-deleters-for-c-smart-pointers.html

+1

Cet objet devient difficile de passer autour sans violer ODR entre les unités de compilation. – Yakk

+0

Oui, vous avez raison. – Hakes

4

11 C++ a un type integral_constant qui fonctionne sur des choses qui ne sont pas comme entier. En C++ 14, un constexpr est renvoyé à la valeur.

Ainsi, en C++ 14, nous pouvons faire:

std::unique_ptr<void, std::integral_constant<decltype(free)*, free>> ptr_c(malloc(10)); 

c'est maladroit. (Cela dépend du fait que () considérera cast-to-function-pointer sur son argument côté gauche).

Nous pouvons coder en dur libre:

using default_free = std::integral_constant<decltype(free)*, free>; 
std::unique_ptr<void, default_free> ptr_c(malloc(10)); 

pour se débarrasser de bruit sur le site d'utilisation.

En C++ 17, on peut écrire une aide:

template<auto t> 
using val = std::integral_constant< std::decay_t<decltype(t)>, t >; 

nous donne:

std::unique_ptr<void, val<free>> ptr_c(malloc(10)); 

qui est plus propre à mon avis.

Live examples.

Nous pouvons écrire un notre propre version en C de 11:

template<class T, std::decay_t<T> t> 
struct val { 
    constexpr operator T() noexcept const { return t; } 
}; 
using default_free = val<decltype(free), free>; 
std::unique_ptr<void, default_free> ptr_c(malloc(10)); 
+0

Wow, c'est cool. Je n'aurais jamais pensé utiliser 'std :: integral_constant' de cette façon. – Quentin

+0

@Quentin Le seul inconvénient est que cela ne fonctionne pas avec l'overridation basée sur l'héritage. – Yakk