# Native Code > برنامه نویسی با C > برنامه نویسی با MFC و ++Visual C > سوال: Linker Error - LNK 2019

## MEHR4N

سلام
من یه مشکلی پیدا کردم تو visual studio ، مشکلم اینه که وقتی از template استفاده میکنم، و constructor رو که تو یه فایل جدا استفاده میکنم Error LNK 2019(unresolved external )l رو میده، کدی که من روش تست کردم اینجوریه:


// test1.h

#ifndef	TEST1_H
#define TEST1_H

namespace TEST
{
	template< class T >
	class MyClass1;

	template< class T >
	class MyClass2
	{
		friend class MyClass1<T>;
	private:
		MyClass2<T> *x;
	};

	template< class T >
	class MyClass1
	{
	public:
		MyClass1();//{ a = NULL; }
	private:
		MyClass2<T> *a;
	};
}

#endif


cpp.*

// test1.cpp

#include "stdafx.h"
#include "test1.h"

namespace TEST
{
	template< typename T >
	MyClass1<T>::MyClass1(){ a = NULL; }
}

 تو MSDN که نگاه میکردم، نوشته بود اگه از template استفاده میکنید، member function ها رو نباید اکسپورت کنید، البته اول Error LNK 2001( unresolved external)l میداد ، بعد که من رفتم msdn رو دیدم اینا رو نوشته بود:
واسه LNK2001:

The definition of member template is outside the class. Visual C++‎ has a limitation in which member templates must be fully defined within the enclosing class. See KB article Q239436 for more information about LNK2001 and member templates.


بعد که رفتم تو more info، اینارو زده بود:

The compiler does not support the use of the "export" keyword as specified in the C++‎ standard below: 
*Section 14*
*Para# 7*: Declaring a class template exported is equivalent to declaring all of its non-inline function members, static data members, member classes, member class templates and non-inline function member templates which are defined in that translation unit exported. 

*Para# 8*: Templates defined in an unnamed namespace shall not be exported. A template shall be exported only once in a program. An implementation is not required to diagnose a violation of this rule. A non-exported template that is neither explicitly specialized nor explicitly instantiated must be defined in every translation unit in which it is implicitly instantiated (temp.inst) or explicitly instantiated (temp.explicit); no diagnostic is required. An exported template need only be declared (and not necessarily defined) in a translation unit in which it is instantiated. A template function declared both exported and inline is just inline and not exported.


من، وقتی تو همون فایل constructor رو تعریف کنم مشکلی ندارم، ولی اگه مثلا توی یه cpp.* دیگه تعریف کنم مشکل داشتم، بعد اونجایی رو که زیرش خط کشیدم دیدم، گفتم شاید اگه تو یه namespace تعریف کنم مشکلی ندارم، ولی بعد از اون error LNK2019 رو داد، بعد help اون رو که نگاه کردم، یه جورایی تو مایه های همون بود این رو نوشته بود:

LNK2019 can also be generated as a result of conformance work that was done for Visual Studio .NET 2003: template friends and specialization. In Visual Studio .NET 2003, a declaration of a friend function with the same name as a function template does not refer to that function template unless template arguments are explicitly specified in the friend declaration.

If you do not specify template arguments, the friend declaration declares a non-template function.


ولی من friend function استفاده نکردم که friend class استفاده کرم :|
البته من visual studio 2008 استفاده میکنم نه 2003 که اینجا زده، ولی همین مشکلا باز هست


کلا نمیدونم با این مشکل چی کار کنم، میشه همه رو یه جا تعریف کرد، ولی مثلا اگه بخوام موقع استفاده از template ها، implementation رو از interface جدا کنم،  این اروره نمیذاره، اگه شما در این رابطه چیزی میدونین و کمکم کنید ممنون میشم.
ببخشید طولانی بود :|

----------


## Nima_NF

این همه توضیح برای این سوال زیاد بود!

کد شما مشکلی ندارد، جر اینکه شما در فایل cpp خودتان اصلا تابع main ندارید که این خطا را ایجاد می کند، برای من بدون مشکل اجرا می شود:

#include <conio.h>
#include <iostream>

#include "stdafx.h"
#include "test.h"


namespace TEST
{
    template< typename T >
    MyClass1<T>::MyClass1(){ a = NULL; }
}

using namespace std;
int main()
{

    _getch();
    return 0;
}

----------


## MEHR4N

نه من خودم main رو گذاشته بودم، فقط فایلش رو اینجا نذاشتم زیاد تر نشه :لبخند گشاده!: 
منم وقتی با مین خالی استفاده میکنم مشکلی ندارم، ولی وقتی instantiate میکنم، چون اون موقع نیاز به constructor داره، ارور رو میده، مثلا اینجوری


#include "test1.h"
int main()
{
	TEST::MyClass1<int> m;
	
	return 0;
}


ارور رو هم این میده: 

Error	1	error LNK2019: unresolved external symbol "public: __thiscall TEST::MyClass1<int>::MyClass1<int>(void)" (??0?$MyClass1@H@TEST@@QAE@XZ) referenced in function _main	Data Structures.obj	Data Structures

البته الان من چک کردم تو فایل مین بذارم، مشکلی پیش نمیاد، ولی تو یه فایل cpp دیگه بذارم مشکل پیش میاد :|

میدونم زیاد بود، آخه من قبل از اینکه اینجا سوالمو بپرسم تو گوگل و خیلی فرومای خارجی دیگه سرچ کرده بودم، به جواب نرسیدم، واسه همین میخواستم کامل مشکلم رو بگم :|

----------


## Saeed_m_Farid

البته با اجازه آقا نیما:



> ولی تو یه فایل cpp دیگه بذارم مشکل پیش میاد


یعنی وقتی instance رو تو یه فایل دیگه ایجاد میکنین مشکل ایجاد میشه؟ یا implementation  رو جای دیگه ای تعریف می کنید؟!



> unresolved external symbol


به نظر میاد شما implementation رو دارین جایی تعریف میکنید که include نکردینش؛ کامپایلر این خطا رو معمولا موقعی میده که نتونه پیاده سازی interface شما رو پیدا کنه : مثلا فرض کنید شما یه type library رو import کنید ولی به کامپایلر فقط header ها رو بدین ...

من هم با vs.net 2008 و هم با BCB6 امتحان کردم مشکلی پیش نیومد البته به شرطی که پیاده سازی سازنده MyClass1 تون رو بذارید جایی که موقع instance درست کردن دیده بشه.

امیدوارم درست منظورتون رو گرفته باشم.

----------


## Nima_NF

template ها کلا محدودیت زیادی دارند و در واقع یک کلاس template یک کلاس واقعی نیست بلکه یک الگویی فراهم می کند که بتوانید هر زمانی که می خواهید با انواع جدید یک نمونه از آن در هنگام فراخوانی بسازید و این زمانی است که یک کلاس ساخته می شود.

از جمله محدودیت های template ها در ++C همین موضوع هست، یعنی اعلان و تعریف متدها باید در همان فایل .h انجام بگیرد (به قولی به صورت inline) و نمی توانید در فایل cpp دیگری انجام دهید. به همین خاطر خطای linker دریافت می کنید. کتابخانه های مبتنی بر template در ++C هم به همین شکل در .h تعریف شده اند.
چون طبق توضیحات فوق، کامپایلر کدهای آن را مانند یک فایل cpp به obj کامپایل نمی کند.

محدودیت های template ها در اسناد هر کامپایلر توضیح داده شده است. مثلا در ++VC در نسخه های اخیر یک سری قابلیت جدید به آن اضافه شده است و محدودیت ها ذکر شده است. بعضی کامپایلر ها export را قبول می کنند و برخی خیر و ...

----------


## MEHR4N

> البته با اجازه آقا نیما:
> یعنی وقتی instance رو تو یه فایل دیگه ایجاد میکنین مشکل ایجاد میشه؟ یا implementation  رو جای دیگه ای تعریف می کنید؟!
> به نظر میاد شما implementation رو دارین جایی تعریف میکنید که include نکردینش؛ کامپایلر این خطا رو معمولا موقعی میده که نتونه پیاده سازی interface شما رو پیدا کنه : مثلا فرض کنید شما یه type library رو import کنید ولی به کامپایلر فقط header ها رو بدین ...
> 
> من هم با vs.net 2008 و هم با BCB6 امتحان کردم مشکلی پیش نیومد البته به شرطی که پیاده سازی سازنده MyClass1 تون رو بذارید جایی که موقع instance درست کردن دیده بشه.
> 
> امیدوارم درست منظورتون رو گرفته باشم.


نه من include کرده بودم، احتمالا همینی که نیما میگه درسته، و اینکه template خودش کلاس نیست و بعد از معلوم شدن نوع، موقع لینک و اینا تازه کامپایلر میخواد  کلاس درست کنه، اینا به ذهن خودم نرسیده بود  :لبخند گشاده!: 
اینم نمیدونستم کتابخونه ها تو خود .h هستن، فکر میکردم اونا رو جدا کردن، واسه همین هی میخواستم اونارو جدا کنم هی فکر میکردم اونا یه کاری کردن جدا باشه.
با این حال ممنون از کمک همه دوستان

----------


## C++‎Lover

دوست عزیز توضیح این مسئله خیلی طولانی میشه. من تا جایی که بتونم به صورت خلاصه در سه پست پشت سر هم توضیح میدم.
نکته: این توضیحات رو بسیار ساده بیان کرده ام و از مطرح کردن بسیاری از جزئیات خودداری کردم. بنابراین اگر نیاز به توضیحات بیشتر دارید بگید تا براتون بنویسم.

نخست باید روند کامپایل یک پروژه رو توسط کامپایلرهای ++C بدونیم. اما قبل از اون باید مفهوم translation unit رو بررسی کنیم.
به بیان ساده به یک فایل با پسوند CPP یا C به همراه سلسله هدرهایی که در آن Include شدن و سلسله هدرهایی که اون هدرها include کردن و الی آخر یک Translation Unit میگن. 

وقتی یک پروژه شروع به کامپایل شدن میکنه ابتدا کلیه این translation unit ها توسط کامپایلر کامپایل شده و فایل obj مربوطه شون ساخته میشه یعنی هر translation unit یک فایل obj. در اینجا وقتی که یک Definition(تعریف) بدون Implementation(پیاده سازی) در این Translation Unit موجود باشه کامپایلر جای اون رو خالی میزاره و اون رو به عنوان external علامت گذاری میکنه. تا اینجا خلاصه کار کامپایلر.

حالا نوبت لینکره. لینکر کلیه فایلهای obj و lib رو دریافت میکنه و با هم قاطی میکنه و فایل اجرایی و یا کتابخانه ای مورد نظر رو تولید میکنه. یکی از کارهایی که لینکر انجام میده پیدا کردن external ها و پر کردن جای اونهاست یعنی resolve کردن. مثلا اگر توی یک translation unit یک external وجود داشت و این external در یک translation unit دیگه پیاده سازی شده باشه لینکر میاد و توی translation unit اولی آدرس external رو به روز رسانی میکنه. حالا مفهوم خطای unresolved external معلوم میشه که یعنی برای مثال یک external وجود داره که در هیچ کدام از translation unit ها پیاده سازی نشده و در این صورت لینکر نمیتونه اون رو resolve کنه پس پیغام خطا unresolved external نشون میده.

----------


## C++‎Lover

در مورد template ها:
در استاندارد ++C نحوه parse شدن template ها توسط کامپایلر به صورت دوفازی است (two-phase name lookup). فاز اول POD Point Of Definition و فاز دوم POI Point Of Instantiation. 

وقتی که کامپایلر به یک template برخورد میکند در فاز POD قرار داره. در این مرحله کامپایلر همه متدها، متغیر ها و نوعهای Non Dependant را کامپایل میکند و Dependant ها را به POI موکول میکند. به طور خیلی ساده Dependant به(member)عضوها و یا (type)نوع هایی گفته میشه که به پارامتر template بستگی دارند. البته استاندارد ++C در این مورد کاملا اکید است و مشخصات کامل dependant ها را به همراه مثال های مربوطه توضیح داده. 
زمانی که کامپایلر به یک (instantiation)نمونه سازی برخورد میکند برای مثال

class1<int> a;

در فاز POI قرار داره. در اینجا کامپایلر میفهمد که که به جای پارامتر template دقیقا چه چیزی باید قرار دهد. (در مثال ما int). در این فاز کامپایلر عضوهای مورد نظر را ساخته و در translation unit جاری قرار میدهد. لازم به یاد آوریست که با پارامترهای مختلف کلاسها و متدهایی که به صورت template هستند معنای مختلفی دارند و به ازای هر پارامتر متد و یا نوع جدیدی ساخته میشود.

اما در اینجا خوبه که این مسئله رو عنوان کنم که کامپایلر ++VC که بیشتر ماها ازش استفاده میکنیم، برخلاف کامپایلرهای مطرح دیگر، در این مورد یعنی two-phase name lookup به همراه بعضی موارد دیگر مثل export template و exception specifications از استاندارد پیروی نمیکند. طرز برخورد ++VC با template ها مثل طرز برخورد آن با ماکروهاست یعنی فقط فاز POI را دارد و فقط وقتی به یک Instantiation برخورد کرد عضوهای مورد نظر را ساخته و در obj مربوط به translation unit جاری قرار میدهد. اگر استاندارد ++C مثل  ++VC با template ها برخورد کرده بود و اونها را به این صورت مثل ماکروها میشناخت اونوقت template های ++C تو بحث OO قرار نمیگرفتند و سلاح خوبی برای طرفدارهای #C میشد که generic ما فلانه و template های شما...

----------


## C++‎Lover

مشکل کد شما:
شما constructor مربوط به نوع MyClass1 را که دارای پارامتر template به نام T است را در فایل test1.cpp قرار داده اید که این فایل در یک translation unit جداگانه کامپایل میشود و در هنگام کامپایل این translation unit چون هیچ گونه instantiate یا نمونه سازی از این کلاس انجام نشده بنابراین هیچ نوع سفارشی از متد constructor شما در translation unit کامپایل شده ساخته نمیشود. برای مثال اگر شما در یک فایل دیگر و در تابع main کد زیر را داشته باشید.

MyClass1<int> a;

یعنی اینکه شئی a را از روی MyClass1 ساخته اید و در همان لحظه متد constructor آن را فراخوانی کرده اید. با توجه به اینکه شما هدر test1.h را در این فایل include کرده اید کامپایلر کلاس MyClass1 را پیدا میکند و آن را به صورت MyClass1<int سفارشی میکند. اما پیاده سازی متد constructor آن یعنی MyClass1<int>::MyClass1 را پیدا نمیکند و آن را به عنوان external علامت گذاری میکند تا لینکر بعدا آن را در سایر translation unit ها پیدا و resolve کند. 
اشکال کار اینجاست که پیاده سازی متد MyClass1<int>::MyClass1 در هیچ translation unit ای وجود ندارد. یادآوری میکنیم که در translation unit کامپایل شده به نام test1.obj متد MyClass1<int>::MyClass1 وجود ندارد بلکه فقط MyClass1<T>::MyClass1 وجود دارد که بی معنی است پس اصلا هیچ نوع MyClass1::MyClass1 در این translation unit هم وجود ندارد. بنابراین compiler نمیتواند آن را resolve کند پس خطای unresolved external را میدهد.

امیدوارم تونسته باشم موضوع رو برسونم.
نگارش بد من رو ببخشید چون عجله ای شد.
پیروز باشید.


برای اطلاعات بیشتر در مورد two-phase name lookup میتوانید به مرجع استاندارد ++C مراجعه نمایید.
برای توضیحات بیشتر در زمینه نحوه کامپایل کردن template ها در ++VC الان مرجع خاصی یادم نمیاد ولی اگر عبارات two-phase name lookup و visual C++‎ رو توی گوگل جستجو کنید حتما پیدا میکنید.

----------


## C++‎Lover

قبل از اینکه کسی اعتراض کنه این نکته رو تاکید میکنم که سعی کردم توضیحاتم رو به صورت خلاصه و قابل لمس ارائه بدم برای مثال مفهوم translation unit در واقع به این صورته:
مفهوم token: یعنی کوچکترین عنصر برنامه ++C که برای کامپایلر معنی داره.
مفهوم translation unit: یک رشته از token ها یک translation unit رو تشکیل میدن.

اما در عمل در یک پروژه ++C یک فایل با پسوند C یا CPP که کامپایلر شروع به کامپایلش میکنه و در این راه تمامی فایلهای include شده رو هم کامپایل میکنه تا یک obj ساخته بشه یک translation unit رو تشکیل میده. اصلا ممکن هم هست که این فایل دارای پسوند C یا CPP نباشه.

----------


## MEHR4N

ممنون از توضیحات بسیار جامع شما، نگارشتون خیلیم خوب بود  :لبخند گشاده!: 
چقدر خوبه یکی  اینجوری اشتباهات آدمو برطرف کنه، در مورد اون هم که گفتین تو اون یکی obj، به جای MyClass1<int>::MyClass ، میاد میبینه MyClass1<T>::MyClass رو میبینه هم درست گفتین، دقیقا مشکل همون بود، آخه من بعد از اینکه اینو خوندم رفتم test1.cpp رو اینجوری نوشتم دیدم کار کرد:

// test1.cpp

#include "stdafx.h"
#include "test1.h"

namespace TEST
{
	MyClass1<int>::MyClass1(){ a = NULL; }
}


ایندفعه دیگه پیدا کرد  :لبخند گشاده!: 

حالا یه سوال، نمیشه یه کاری کرد، 2 تا cpp تو یه unit قرار بگیرن؟
اگه اون کدامو (code) توی یه کامپایلر دیگه همونجوری مینوشتم چی ارور میداد یا نه؟ :|

----------


## C++‎Lover

خوشحالم که مفید بود.




> نمیشه یه کاری کرد، 2 تا cpp تو یه unit قرار بگیرن؟


اگر بخواهید دو تا فایل cpp تویه یک translation unit قرار بگیره همون جوری که یه فایل h رو include میکنید میتونید یه فایل cpp رو هم توی یک فایل دیگه include کنید. ولی این کار توصیه نمیشه.

در ضمن اون کاری که شما کردید یعنی یک متد constructor سفارشی برای کلاستون با پارامتر int درست کردید و دیگه کلاستون برای پارامترهای دیگه constructor نداره و این کار درست نیست چون با این کار در واقع ماهیت الگویی بودن کلاستون رو که مهمترین برتری استفاده template هاست از بین میبرید و اگر یک شئی نمونه از کلاس با پارامتر template مثلا float بسازید دوباره با همون پیغام خطا روبرو میشید و هر دفعه مجبورید کلاس رو ویرایش کنید و constructor های سفارشی جدیدی اضافه کنید که کار درستی نیست.

بهترین مدل برای سازماندهی کدهای template ها که توسط اکثر جامعه ++C استفاده میشه Inclusion Model یا مدل شمول نام داره که در این روش به سادگی کل کد مربوط به تعریف اعلان و پیاده سازی template رو در هر translation unit ای که از template نمونه سازی میکنه include میکنید. مثلا کل template رو توی یک هدر تعریف و پیاده سازی کنید و سپس این فایل رو توی فایلهای cpp که نیاز به این template دارن include کنید. 

مدل دیگری هم به نام Separation Model یا مدل جداسازی وجود داره که استفاده از این مدل در حال حاضر غیر عملیه چون احتیاج به کلیدواژه export داره که در حال حاضر هیچ کدوم از کامپایلر های مطرح تجاری دنیا به غیر از EDG based Comeau از آن پشتیبانی نمیکنند. توضیح اینکه کلید واژه export جزء استاندارد ++C هست اما به دلیل پیچیدگی پیاده سازیش و زمان و نیروی انسانی زیادی که برای پیاده سازی این کلیدواژه نیازه، تقریبا هیچ کدوم از سازندگان کامپایلرها زیر بارش نرفتن و حتی پیشنهادی برای حذف این کلیدواژه از استاندارد توسط Herb Sutter که یکی از آرشیتکت های ++Visual C و همچنین یکی از اعضای کمیته استاندارد ++C هست بیان شد که این پیشنهاد توسط کمیته استاندارد ++C رد شد. همچنین تقریبا بعیده که کامپایلری در آینده این کلیدواژه رو پیاده سازی کنه.




> اگه اون کدامو (code) توی یه کامپایلر دیگه همونجوری مینوشتم چی ارور میداد یا نه؟ :|


بله. در یک کامپایلر استاندارد چه از two phase name lookup پشتیبانی بکنه چه نکنه پیغام خطا دریافت میکنید. ولی ممکنه شکل پیغام فرق بکنه.

----------


## C++‎Lover

یک راه دیگه هم دارید. اونم اینه که همون جوری مثل قبل اعلانهای template رو توی فایل h نگه دارید و پیاده سازی ها رو در فایل cpp سپس در یک فایل دیگه به صورت صریح از template نمونه سازی کنید.
برای مثال یک فایل جدید در پروژه درست کنید به نام مثلا tempinst.cpp

//tempinst.cpp

#include "MyClass1.cpp"

template class MyClass1<int>;



این روش یک مزیت داره اونم اینه که هر وقت یک سفارشی سازی جدید به این فایل اضافه میکنید و کامپایلر اونو کامپایل میکنه کد definition کلاس template شما در این translation unit ایجاد میشه و بعدا لینکر از این translation unit استفاده میکنه و دیگه translation unit اصلیتون یا اونی که باهاش کار میکنید بزرگ نمیشه.
توجه کنید اگر هر دفعه همه template ای رو که توی هر translation unit ای که لازم دارید Include کنید حجم تک تک این translation unit ها بزرگ میشه و کامپایلشون طولانی تر میشه. ولی اگر definition های template رو توی یک فایل cpp بزارید و سپس از این روش استفاده کنید definition های template برای هر پارامتر فقط یک بار در این فایل جدید کامپایل میشن و بعدا لینکر بقیه کارها رو برای translation unit های دیگه انجام میده که برای کتابخانه های بزرگ کلی زمان کامپایل رو پایین میاره. به هر حال این روش یه بدی هم داره و اونم اینه که کل template و توابعش در translation unit که جدید اضافه کردیم کامپایل میشن نه فقط توابعی که استفاده میکنیم.

نمیدونم تونستم برسونم یا نه.

----------

