9
votes

Erreur de liaison étrange C ++

Lorsque j'essaie de compiler cela,

template <class T>
T min(T const a, T const b){
    return a < b ? a : b;
}


6 commentaires

Pourquoi GCC et CLang sont-ils marqués? Vous ne devriez utiliser qu'un seul compilateur qui, par l'apparence, est collée


ideone.com/ypq6w fonctionne pour moi


Essayez de changer votre min pour prendre les paramètres par référence.


Vos exemples doivent avoir #include pour std :: min


Puisque C ++ 11 Une autre solution de contournement consiste à utiliser la liste de la liste de min: std :: min ({k.a, 7}) . Je pense que ce n'est pas une utilisation ODR car les répertoriennes initialisées copient de la valeur. Depuis C ++ 14, le formulaire de liste donne même un consexpr .


Voir aussi la réponse à Stackoverflow.com/Questtions/9928373/...


3 Réponses :


2
votes

Cette question est posée assez souvent. Je crois que c'est un bug de collage. A code> est désactivé comme une expression constante trop tôt et le compilateur ne génère pas de définition de celui-ci. Strike> (voir correction dans les commentaires)

std :: min Code> prend ses arguments par const Référence, une définition doit donc exister. P>

#include <iostream>

struct K{
    constexpr static int a() { return 5; }
};


int main(){
    K k;

    std::cout << std::min(k.a(), 7);
}


5 commentaires

IIRC aussi le niveau d'optimisation pourrait affecter cela.


@juzzlin Il ne lie pas sur Apple Clang sans optimisation. Le compilateur n'empêche pas de déterminer l'utilisation des ODR d'une option correctement.


@RICHARD HODGES OK, je me souviens juste que j'ai rencontré Ceci avec GCC sur Ubuntu et cela fonctionnait sans optimisations, mais j'ai échoué avec -O3 ou quelque chose comme ça: p


Non, ce n'est pas un bug (je pensais que c'était aussi ...). Mais une initialisation intégrale de consons statique n'est toujours pas une définition selon la norme: 9.4.2 Membres de données statiques [Class.Static.Data] §3 Si un élément de données statique de const non volatil est de type intégré ou d'énumération, son Déclaration dans la définition de la classe peut spécifier une initialisation de la corse ou égale dans laquelle chaque clause d'initialisation qui est une affectation experte est une expression constante ... Le membre doit toujours être défini dans une portée d'espace de noms s'il est odrique Utilisé (3.2) ... et la définition de la portée de l'espace de noms ne doit pas contenir d'initialisateur.


@Sergeballesta correction faite à la poste en réponse. Merci.



8
votes

std :: min accepte des arguments par référence. Lier une référence à un objet signifie que l'objet est ODR-utilisé (il existe un échantillon de code dans [basic.def.odr] / 2 à peu près identique à votre échantillon).

Cependant, dans le (int) k.a cas, k.a n'est pas odr-utilisé ;; Parce qu'il effectue une conversion de lvalue à rvalue qui donne une expression constante. (Il y a quelques autres conditions ici aussi, mais votre code est correct).

Si un objet est ODR-utilisé , il doit y avoir exactement une définition de celui-ci; sans diagnostic requis pour violer cette règle. Donc le premier cas peut être accepté ou non; et le deuxième cas doit être accepté.

Dans votre propre version de min , il prend des arguments par valeur, ce qui est similaire à celui de (int) ka - la seule action prise sur ka Il y a une conversion de rvalue pour initialiser le paramètre de votre min .

Vous pouvez lire l'ensemble complet des règles sur ODR-Use dans la section [basic.def.odr] d'un brouillon standard C ++.


3 commentaires

une ligne const int k :: a; est ce qui manque


@ SP2Danny pas nécessairement; par exemple. Le deuxième échantillon est correct sans cette ligne


BTW, 9.4.2 Membres de données statiques [Class.Static.Data] §3 est spécifiquement explicite pour ce cas d'utilisation si un élément de données statique de const non volatil est de type intégré ou d'énumération, sa déclaration dans la définition de la classe peut Spécifiez une initialisation de la corset ou égale dans laquelle chaque clause d'initialisateur qui est une affection qui est une expression constante est une expression constante ... L'élément doit toujours être défini dans une portée d'espace de noms si elle est ODR-utilisée ( 3.2) Dans le programme et la définition de portée de l'espace de noms ne contiennent pas d'initialisateur.



-2
votes

Vous avez déclaré une variable statique (A) dans votre structure, mais vous ne l'avez pas définie.

struct K
{
    const static int a; // declaration
};

const int K::a = 5; // definition 


int main()
{
    std::cout << std::min(K::a, 7);
}


2 commentaires

Cela ne fonctionnerait pas pour la même raison que la tentative de OP ne fonctionnerait pas.


Vous avez raison. J'utilisais g ++ -std = C ++ 11 -O2 -Wall et cela a fonctionné. Supprimer -O2 Le faire ne le faire pas. Je vais éditer la réponse.