PDA

View Full Version : مبتدی: مشکل در تشخیص نوع متغیر ارسالی به تابع



ehsan_faal
شنبه 06 تیر 1394, 06:07 صبح
سلام دوستان.
من یه template درست کردم که توی تابع هام استفاده کنم.
بر اساس اون template میام و یه وکتور درست میکنم و میدم به یه تابعی تا مقادیر اولیه رندوم بریزه توش.
مشکل اینجاست که با این که ظاهر کد درسته و توی تابع شرایطی رو قرار دادم که بر اساس نوع متغیرهای ارسالی به تابع مقادیر رندوم رو تولید کنه، بازم میره و قسمتهایی رو که نباید استفاده بشه رو استفاده میکنه.


#include <iostream>
#include <vector>
#include <random>
#include <time.h>
#include <algorithm>
#include <iomanip>
#include <type_traits>
using namespace std;
template<typename T = int>
using Matrix = vector<vector<T>>;

template<typename T>
void InitializeMatrix(Matrix<T> &UserMatrix, T MinRange, T MaxRange)
{
static_assert((is_integral<T>::value | is_floating_point<T>::value), "Input Type can be only instantiated with integer or floating point types");
default_random_engine eng(time(0));
if (is_same<T,int>::value)//(is_integral<T>::value)
{
for (auto& row : UserMatrix)
{
uniform_int_distribution<T> rng(MinRange, MaxRange);
for_each(row.begin(), row.end(), [&rng, &eng](T& element){
element = rng(eng);
});
random_shuffle(row.begin(), row.end());
}
}
else if (is_same<T, double>::value)//(is_floating_point<T>::value)
{
for (auto& row : UserMatrix)
{
uniform_real_distribution<T> rng(MinRange, MaxRange);
for_each(row.begin(), row.end(), [&rng, &eng](T& element){
element = rng(eng);
});
random_shuffle(row.begin(), row.end());
}
}
}


template<typename T>
void DisplayMatrix(Matrix<T> &matrix)
{
for (auto& row : matrix)
{
for_each(row.begin(), row.end(), [](T& element){
cout << setw(8) << setiosflags(ios::left) << element;
});
cout << endl;
}
}

int main()
{
Matrix<int> a(2, vector<int>(3, 0));
InitializeMatrix(a, -10, 35);
DisplayMatrix(a);

return 0;
}



مشکل اینجاست که با این که من int رو انتخاب کردم ولی همش میاد و خط 32 رو چک میکنه.
ضمنا چرا وقتی من یه همچین کدی داشته باشم خطا میگیره(بالفرض وکتور از نوع دابل و رنجها رو int بدم،مگه تبدیل int به دابل اتوماتیک نباید انجام بشه؟)
int row=2,column=3;
Matrix<double> Mat(row, vector<double>(column, 0));
InitializeMatrix(Mat, -3, 5);
DisplayMatrix(Mat);

ولی این یکی درسته(یعنی از نحوه call کردن توابع ایراد نمیگیره فقط همون ایرادهای قبلی باقی میمونه)
int row=2,column=3;
Matrix<double> Mat(row, vector<double>(column, 0));
InitializeMatrix(Mat, -3.0, 5.0);
DisplayMatrix(Mat);

rahnema1
شنبه 06 تیر 1394, 12:55 عصر
سلام
به علت اینه که ما در این مورد نمی تونیم به کامپایلر بگیم عمل کامپایل را به صورت مشروط انجام بده
با استفاده از دستورات پیش پردازنده می شه کامپایل مشروط کرد همچنین یک راه حل استفاده از enable_if هست
یه سوال آیا ضرورتی داره که rng داخل حلقه تعریف شده؟

#include <iostream>
#include <vector>
#include <random>
#include <time.h>
#include <algorithm>
#include <iomanip>
#include <type_traits>
using namespace std;
template<typename T = int>
using Matrix = vector<vector<T>>;

//template <class T>
//struct Matrix_Operation{
template<class T >
void InitializeMatrix(Matrix<T> &UserMatrix, T MinRange, T MaxRange, typename std::enable_if<std::is_integral<T>::value, T>::type = 0)
{
default_random_engine eng(time(0));
for (auto& row : UserMatrix)
{
uniform_int_distribution<T> rng(MinRange, MaxRange);
for_each(row.begin(), row.end(), [&rng, &eng](T& element){
element = rng(eng);
});
random_shuffle(row.begin(), row.end());
}
}

template< class T >
void InitializeMatrix(Matrix<T> &UserMatrix, T MinRange, T MaxRange, typename std::enable_if<std::is_floating_point<T>::value, T>::type = 0)
{
default_random_engine eng(time(0));
for (auto& row : UserMatrix)
{
uniform_real_distribution<T> rng(MinRange, MaxRange);
for_each(row.begin(), row.end(), [&rng, &eng](T& element){
element = rng(eng);
});
random_shuffle(row.begin(), row.end());
}
}

template<typename T>
void DisplayMatrix(Matrix<T> &matrix)
{
for (auto& row : matrix)
{
for_each(row.begin(), row.end(), [](T& element){
cout << setw(8) << setiosflags(ios::left) << element;
});
cout << endl;
}
}

int main()
{
Matrix<int> a(2, vector<int>(3, 0));
InitializeMatrix(a, -10, 35);
DisplayMatrix(a);
return 0;
}

ehsan_faal
شنبه 06 تیر 1394, 15:32 عصر
ooops! در مورد rng حق با شماست، سوتی دادم.
در مورد مشکل دوم نظری ندارید؟

rahnema1
شنبه 06 تیر 1394, 19:22 عصر
ooops! در مورد rng حق با شماست، سوتی دادم.
در مورد مشکل دوم نظری ندارید؟

تبدیل مرله بعد هست فعلا در مرحله ترجمه پارامترهای تمپلیت چک می شن که مناسب هستند یا نه اگه نبود هنگام کامپایل خطا می گیره
اینجور می تونید حل کنید که min و max هر کدوم یک نوع متفاوت باشه

template< class T , class T1, class T2>
void InitializeMatrix(Matrix<T> &UserMatrix, T1 MinRange, T2 MaxRange, typename std::enable_if<std::is_floating_point<T>::value, T>::type = 0)

ehsan_faal
یک شنبه 07 تیر 1394, 00:15 صبح
در مورد enable_if هم یه سوال داشتم:
اگه مقدار بولین اولی درسته باشه حاصلش میشه نوعی که تو پارامتر دوم بهش میدیم:
132664
یعنی الان من اگه ماتریس دابل داشته باشم یه پارامتر چهارمی از نوع دابل به این تابع اضافه میشه؟
قسمت آخرش چیه؟(type=0)

rahnema1
یک شنبه 07 تیر 1394, 14:18 عصر
وقتی که یک سری تعریف تابع (تمپلیت) مشابه وجود داشته باشند یا به عبارت دیگه overload شده باشند و در جایی یکی از اینها صدا زده بشه کامپایلر باید بتونه یکی از اونها که مناسبترین مورد هست را پیدا کنه
در موردی که تابعها تمپلیت باشند کامپایلر در تک تک اونها جستجو می کنه ببینه تابعی که صدا زده شده با کدام یک از تمپلیت ها مطابقت می کنه اگه با هیچ کدام مطابق نبود خطا صادر می شه از جمله مواردی که باعث می شه یک تمپلیت با پارامترهای خاص خودش به عنوان تمپلیت مناسب توسط کامپایلر انتخاب نشه اینه که در صورتی که اون تمپلیت انتخاب میشد این پارامتر باعث ایجاد یک «نوع» غیرمنطقی می شد
فلسفه استفاده از enable_if برای اینه که به طور گزینشی در تمپلیت خطا ایجاد کنیم که دیگه کامپایلر وقتی ببینه اگه این تمپلیت را انتخاب کرد با خطا مواجه می شه اون را از لیست تابع های مورد نظرش بیرون بیاره و تابعهای دیگه را جهت انتخاب مورد بررسی قرار بده
تعریف enable_if به صورت زیر هست:

template <bool B, class T = void>
struct enable_if {
typedef T type;
};

template <class T>
struct enable_if<false, T> {};

یک کلاس تمپلیت که داخلش یک عضو اعلان می کنه به نام type که در واقع اسم دیگری برای T هست. در واقع این عضو، خودش «نوع» هست.
اینجا یک تخصصی کردن تمپلیت داریم وقتی که پارامتر اول false باشه کلاس خالی تعریف می شه که هیچ عضوی نداره
بنابراین در حالتی که این تمپلیت مورد استفاده قرار بگیره و پارامتر اولش false باشه دیگه عضوی به نام type در داخل خودش نداره در سایر موارد ( وقتی که پارامتر true باشه) عضو type برای اون کلاس تعریف می شه
حالا بریم سر مثال خودمون
ما در تابع main تابع InitializeMatrix را به صورتی صدا می زدیم که آرگومانهای اون از نوع int باشن
کامپایلر تمام تابعها را جستجو می کنه تا مورد مناسب را انتخاب کنه
فرض کنید اول تابع مربوط به floating point را بررسی کنه

typename std::enable_if<std::is_floating_point<T>::value>::type

اینجا is_floating_point مقدار false بر می گردونه که در نتیجه enable_if خالی انتخاب می شه که ما اگه بخواهیم عضو type:: اون را انتخاب کنیم، چنین عضوی وجود نداره (خطا) و تابع InitializeMatrix مربوط به floating توسط کامپایلر مردود می شه
typenmae هم برای اینه که اگه یک عضو وابسته به پارامتر تمپلیت، خودش «نوع» باشه لازمه typename بیاد
حالا کامپایلر می ره سراغ تابع مربوط به is_integral که با خطایی مواجه نمی شه و type همون int هست و پارامتر زیر

typename std::enable_if<std::is_integral<T>::value, T>::type = 0

تبدیل می شه به int = 0
یعنی پارامتر بدون نام ( بدون استفاده) از نوع int که مقدار پیش فرض اون برابر صفر هست. اینجا تابع 4 تا پارامتر داره اما پارامترهای با مقدار پیش فرض را می تونیم هنگام صدا زدن تابع استفاده نکنیم یعنی همون 3ه تا پارامتر اصلی به کار ببریم
ضمنا می شد از اشاره گر هم استفاده کنیم مثلا به صورت زیر

typename std::enable_if<std::is_integral<T>::value, T>::type* = 0

یا

typename std::enable_if<std::is_integral<T>::value>::type* = 0

که در مورد دوم اشاره گر از نوع void می شد
همچنین می شه از enable_if درقسمت نوع برگشتی تابع استفاده کرد به صورت زیر

template<class T >
typename std::enable_if<std::is_integral<T>::value>::type
InitializeMatrix(Matrix<T> &UserMatrix, T MinRange, T MaxRange)
//...
template< class T >
typename std::enable_if<std::is_floating_point<T>::value>::type
InitializeMatrix(Matrix<T> &UserMatrix, T MinRange, T MaxRange)

که باز هم ترفند enable_if همون خاصیت ایجاد خطای عمدی را داره ضمن اینکه به عنوان نوع برگشتی هم ازش استفاده شده
در منبع زیر توضیحات خوبی توسط کسی که این ترفند را ارائه کرده داده شده
http://www.drdobbs.com/function-overloading-based-on-arbitrary/184401659

ehsan_faal
یک شنبه 07 تیر 1394, 16:14 عصر
واقعا عالی بود.
ممنون