web-dev-qa-db-fra.com

Initialisation des membres pour la variable non copiable en C ++ 17

Lors de l'initialisation d'un membre pour une variable non copiable (telle que std::atomic<int>), il est nécessaire d'utiliser direct-initialization plutôt que copy-initialization selon la réponse ici . Cependant, lorsque j'allume -std=c++17 dans g++ 7.4.0, il semble que ce dernier fonctionne aussi bien.

#include <atomic>

class A {
    std::atomic<int> a = 0;     // copy-initialization
    std::atomic<int> b{0};      // direct-initialization
};
$ g++ -c atomic.cc -std=c++11    // or c++14
atomic.cc:4:26: error: use of deleted function ‘std::atomic<int>::atomic(const std::atomic<int>&)’
     std::atomic<int> a = 0;     // copy-initialization

$ g++ -c atomic.cc -std=c++17
// no error

Il a également échoué lors de la compilation avec g++ 6.5.0 même avec -std=c++17. Lequel est correct ici?

16
zingdle

Le comportement a changé depuis C++ 17, qui oblige les compilateurs à omettre la construction copier/déplacer dans std::atomic<int> a = 0;, C'est-à-dire = élision de copie garantie .

(c'est moi qui souligne)

Dans les circonstances suivantes, les compilateurs sont tenus d'omettre la construction copier/déplacer des objets de classe, même si le constructeur copier/déplacer et le destructeur ont des effets secondaires observables. Les objets sont construits directement dans le stockage où ils seraient autrement copiés/déplacés. Les constructeurs de copie/déplacement n'ont pas besoin d'être présents ou accessibles, car les règles de langage garantissent qu'aucune opération de copie/déplacement n'a lieu, même conceptuellement :

En détails, std::atomic<int> a = 0; Effectue initialisation de la copie :

Si T est un type de classe et que la version non qualifiée cv du type de l'autre n'est pas T ou dérivée de T, ou si T est un type non classe, mais que le type de l'autre est un type classe, des séquences de conversion définies par l'utilisateur qui peuvent convertir du type autre en T (ou en un type dérivé de T si T est un type de classe et une fonction de conversion est disponible) sont examinés et le meilleur est sélectionné par la résolution de surcharge. Le résultat de la conversion, qui est une prvalue temporary (until C++17)prvalue expression (since C++17) si un constructeur de conversion a été utilisé, est ensuite utilisé pour initialiser directement l'objet.

et

(c'est moi qui souligne)

si T est un type de classe et que l'initialiseur est une expression de valeur dont le type non qualifié cv est la même classe que T, l'expression d'initialisation elle-même, plutôt qu'une expression matérialisée temporaire, est utilisée pour initialiser l'objet de destination

Cela signifie que a est initialisé directement à partir de 0, Il n'y a pas de temporaire à construire, puis plus de temporaire à copier/déplacer depuis.

Avant C++ 17, dans le concept std::atomic<int> a = 0;, Un std::atomic Temporaire doit être construit à partir de 0, Puis le temporaire est utilisé pour copier-construire a.

Même copie élision est autorisé avant C++ 17, il est considéré comme une optimisation:

(c'est moi qui souligne)

Il s'agit d'une optimisation: même lorsqu'elle a lieu et que le constructeur copy/move (since C++11) n'est pas appelé, elle doit toujours être présente et accessible (comme si aucune optimisation ne s'était produite) ), sinon le programme est mal formé :

C'est pourquoi gcc déclenche un diagnostic en mode pré-c ++ 17 pour std::atomic<int> a = 0;.

(c'est moi qui souligne)

Remarque: la règle ci-dessus ne spécifie pas d'optimisation: la spécification du langage de base C++ 17 des valeurs et des temporaires est fondamentalement différente de celle des révisions C++ antérieures: il n'y a plus de temporaire à copier/déplacer de . Une autre façon de décrire la mécanique C++ 17 est le "passage de valeur non matérialisé": les valeurs sont retournées et utilisées sans jamais matérialiser un temporaire .

BTW: Je suppose qu'il y avait un bogue dans g++ 6.5.0 Avec -std=c++17; et il a été corrigé dans une version ultérieure.

16
songyuanyao

Laquelle est correcte ici?

Le 7.4.0 est correct. La copie peut être élidée pour ce cas, c'est pourquoi elle est OK. (bien que cela nécessite c ++ 17 ).

(voir https://en.cppreference.com/w/cpp/language/copy_initialization pour plus de détails)

3
darune