Considérez ces deux approches qui peuvent représenter une "option". int
" :
using std_optional_int = std::optional<int>;
using my_optional_int = std::pair<int, bool>;
Étant donné ces deux fonctions...
auto get_std_optional_int() -> std_optional_int
{
return {42};
}
auto get_my_optional() -> my_optional_int
{
return {42, true};
}
...les deux g++ tronc et tronc clang++ (avec -std=c++17 -Ofast -fno-exceptions -fno-rtti
) produire l'assemblage suivant :
get_std_optional_int():
mov rax, rdi
mov DWORD PTR [rdi], 42
mov BYTE PTR [rdi+4], 1
ret
get_my_optional():
movabs rax, 4294967338 // == 0x 0000 0001 0000 002a
ret
exemple en direct sur godbolt.org
Pourquoi est-ce que get_std_optional_int()
nécessitent trois mov
les instructions, tandis que get_my_optional()
n'a besoin que d'un seul movabs
? S'agit-il d'un problème de qualité d'interface utilisateur, ou y a-t-il quelque chose dans std::optional
qui empêche cette optimisation ?
Veuillez également noter que les utilisateurs des fonctions peuvent être complètement optimisés de toute façon :
volatile int a = 0;
volatile int b = 0;
int main()
{
a = get_std_optional_int().value();
b = get_my_optional().first;
}
...résulte en :
main:
mov DWORD PTR a[rip], 42
xor eax, eax
mov DWORD PTR b[rip], 42
ret
6 votes
optional
est retourné par un pointeur caché, ce qui signifie que la définition du type contient quelque chose qui interdit de le retourner par un registre.1 votes
La différence évidente est que
std::pair
est un agrégat, tandis questd::optional
ne l'est pas. Je ne sais pas si ça devrait avoir un effet, mais vous savez...0 votes
Non.
optional
doivent allouer un espace de stockage correctement dimensionné et aligné, puis utiliser le nouveau placement pour construire réellement la valeur ?3 votes
Même problème avec
boost::optional
Il est possible d'utiliser n'importe quelle version de GCC, sans avoir besoin de C++17 pour la démo : godbolt.org/g/MV14mr3 votes
Le typage agrégé/non agrégé, l'ABI SYS V x64 et le fait que 4294967338 est 0x10000002a devraient rendre cela clair.
1 votes
Pourquoi les non-agrégats doivent-ils être retournés sur la pile ?
1 votes
Vous le savez probablement, mais strictement en parlant de votre "optionnel" n'est pas du tout optionnel. L'utilisation d'une paire fonctionne avec des types comme
int
mais aura une sémantique non-optionnelle avec par exemple des types non-constructibles par défaut.1 votes
@PasserBy : Ce n'est pas sur la pile, c'est à travers un pointeur caché (qui pourrait pointer où l'appelant veut qu'il pointe, par exemple dans un objet existant en mémoire). Je pense que l'idée est que chaque instance de l'objet a toujours une adresse, parce que la construction de l'objet de la valeur de retour pourrait incorporer des pointeurs en elle, de sorte que l'appelant a besoin de connaître cette adresse.
0 votes
Je ne pense pas que ce soit la construction qui soit plus chère. Je pense que c'est le retour qui est le plus cher. Si vous essayez de le construire sans le retourner, vous verrez peut-être des résultats très différents.
0 votes
Pouvez-vous essayer folly::Optional<> ? github.com/facebook/folly/blob/master/folly/Optional.h
3 votes
@WojciechMigda
folly::Optional
n'a pas la magie nécessaire pour rendre ses fonctions membres spéciales conditionnellement triviales. (Il viole aussi l'ODR en utilisant la liaison interneNone
dans les fonctions en ligne, et chaqueconstexpr
oFOLLY_CPP14_CONSTEXPR
est mal formée NDR : vous ne pouvez pas implémenter la fonctionoptional
'sconstexpr
API avecaligned_storage
.) +1 pour êtreco_await
- mais ils feraient mieux de voler leoptional
de la gamme-v3 et en ajoutant le reste de leur API.