2017-06-16 10 views
1

Je suivais ce tutoriel - http://www.learncpp.com/cpp-tutorial/132-function-template-instances/modèle de fonction par valeur et par différence de référence

// passing all parameters by references 
template <typename T1, typename T2> 
const T2& add_two_objects(const T1& x,const T2& y) { 
     return x+y; 
}; 

int main() { 
    using std::cout; 
    int x(0),y(0); 
    std::cout << "Please enter number 1" << std::endl; 
    std::cin >> x; 
    std::cout << "Please enter number 2" << std::endl; 
    std::cin >> y; 
    cout<< "sum of two integers is " << add_two_objects(x,y+1.2) << '\n'; 
    cout<< "sum of two double is " << add_two_objects(x,y+2.52424324) << '\n'; 
    return 0; 
} 

Le programme compile très bien, mais au moment de l'exécution, je reçois toujours une erreur de segmentation. Cependant, si je change le modèle pour passer en valeur, alors tout fonctionne.

// passing all parameters by value 
template <typename T1, typename T2> 
const T2 add_two_objects(const T1 x,const T2 y) { 
     return x+y; 
}; 

Quelqu'un peut-il expliquer?

+2

Vous ne pouvez pas retourner une référence à un temporaire. Mais c'est un code assez inutile, et assez mal écrit. Peut-être que vous devriez trouver un tutoriel différent? –

+0

@CodyGray au moins le turial a un 'using std :: cout' au lieu de' using namespace std; ' – user463035818

+0

J'ai modfied les exemples de tutoriel pour essayer des choses plus complexes (pour comprendre bien sûr) Le tutoriel je pense est assez bon. – infoclogged

Répondre

6

Ce

template <typename T1, typename T2> 
const T2& add_two_objects(const T1& x, const T2& y) { 
     return x+y; 
}; 

renvoie une référence à un temporaire. Faire le retour une valeur

template <typename T1, typename T2> 
T2 add_two_objects(const T1& x, const T2& y) { 
     return x+y; 
}; 

et vous devriez aller bien. Btw, il n'est pas clair que renvoyer un T2 est la meilleure chose à faire ici. Considérons par exemple le cas T1=size_t et T2=char. Mieux vaut retourner le type que l'opération génère effectivement x+y

template <typename T1, typename T2> 
auto add_two_objects(const T1& x, const T2& y) 
-> decltype(x+y) 
{ 
    return x+y; 
}; 

------ ----- modifier

Vous ne doit pas renvoyer une référence à un objet temporaire. C'est un BUG. Si vous voulez un bug, allez-y. Et vous ne devriez pas utiliser de mauvais tutoriels. Il y a des situations où vous voulez/devriez renvoyer une référence, mais ce n'est pas l'un d'entre eux. Ce sont quand la référence est à un objet qui ne sera pas détruit (ou ne sortira pas de la portée) lors du retour de la fonction (comme le feront toutes les variables automatiques et temporaires).

+0

mais je voudrais retourner une référence et non la valeur comme suggéré par votre première solution. Est-ce que cela signifie que le code du tutoriel est faux? – infoclogged

+0

@infoclogged vous souhaitez renvoyer une référence à quoi? Une référence à une variable dans le cadre de la méthode est inutile en dehors de la portée de la méthode – user463035818

+0

Bon, j'ai compris maintenant. Si j'instancie un objet statique, la référence ne pointe plus vers un objet temporaire. Ainsi, au lieu de renvoyer (x + y), je pourrais déclarer une variable statique (statique T2 z;) dans le modèle de fonction, puis renvoyer la référence (z = x + y; return z;). Bien sûr, ce n'est pas un vrai code, mais juste pour comprendre les bases. – infoclogged

1

Pour le rendre plus clair, enveloppons les entiers dans les structures.

Voici un programme démonstratif

#include <iostream> 

struct A 
{ 
    A(int x) : x(x) {} 
    ~A() { std::cout << "[A::~A() is called for x = " << x << ']' << std::endl; } 
    int x; 
}; 

A operator +(const A &lhs, const A &rhs) 
{ 
    return A(lhs.x + rhs.x); 
} 

std::ostream & operator <<(std::ostream &os, const A &a) 
{ 
    return os << a.x; 
} 

template <typename T1, typename T2> 
const T2& add_two_objects(const T1& x,const T2& y) { 
     return x+y; 
}; 

int main() 
{ 
    std::cout<< "sum of two integers is " << add_two_objects(A(1), A(2)) << '\n'; 

    return 0; 
} 

Sa sortie pourrait ressembler

prog.cc:22:18: warning: returning reference to temporary [-Wreturn-local-addr] 
     return x+y; 
       ^
sum of two integers is [A::~A() is called for x = 3] 
Segmentation fault 

tout d'abord le compilateur avertit que la fonction retourne référence à une valeur temporaire. C'est après avoir quitté la fonction que l'objet temporaire sera détruit et cette sortie

[A::~A() is called for x = 3] 

le confirme. En conséquence, la référence sera invalide et le programme aura un comportement indéfini.

En fait, vous pouvez imaginer la logique du programme de la manière suivante

int main() 
{ 
    const A &r = add_two_objects(A(1), A(2)); 
    std::cout<< "sum of two integers is " << r << '\n'; 

    return 0; 
} 

Sa sortie ressemble presque similaire dans le programme ci-dessus

prog.cc:22:18: warning: returning reference to temporary [-Wreturn-local-addr] 
     return x+y; 
       ^
[A::~A() is called for x = 3] 
[A::~A() is called for x = 1] 
[A::~A() is called for x = 2] 
Segmentation fault 

C'est la référence devient invalide.

Si pour supprimer la référence dans la déclaration de fonction

template <typename T1, typename T2> 
const T2/*&*/ add_two_objects(const T1& x,const T2& y) { 
     return x+y; 
}; 

alors la sortie du programme pourrait ressembler à

sum of two integers is 3 
[A::~A() is called for x = 3] 
[A::~A() is called for x = 1] 
[A::~A() is called for x = 2] 
+0

Y at-il une raison particulière pour laquelle il y a un avertissement pour une structure, mais pas pour 'int's? – user463035818

+0

@ tobi303 La raison peut être que la structure a un constructeur et un destructeur. –