Я пытаюсь определить, какая версия функции-члена вызывается на основе параметра шаблона класса. Я попробовал это:
#include <iostream>
#include <type_traits>
template<typename T>
struct Point
{
void MyFunction(typename std::enable_if<std::is_same<T, int>::value, T >::type* = 0)
{
std::cout << "T is int." << std::endl;
}
void MyFunction(typename std::enable_if<!std::is_same<T, int>::value, float >::type* = 0)
{
std::cout << "T is not int." << std::endl;
}
};
int main()
{
Point<int> intPoint;
intPoint.MyFunction();
Point<float> floatPoint;
floatPoint.MyFunction();
}
я подумал: «используйте первую MyFunction, если T — int, и используйте вторую MyFunction, если T — не int, но я получаю ошибки компилятора, говорящие« error: нет типа с именем ‘type’ в ‘struct std :: enable_if’ » Кто-нибудь может указать, что я здесь делаю не так?
enable_if
работает потому что подстановка аргумента шаблона привела к ошибке, и поэтому замена отбрасывается из набора разрешений перегрузки, и компилятор рассматривает только другие возможные перегрузки.
В вашем примере не происходит подстановка при создании экземпляров функций-членов, потому что аргумент шаблона T
уже известно в то время. Самый простой способ добиться того, что вы пытаетесь сделать, — создать фиктивный аргумент шаблона, который по умолчанию равен T
и использовать это для выполнения SFINAE.
template<typename T>
struct Point
{
template<typename U = T>
typename std::enable_if<std::is_same<U, int>::value>::type
MyFunction()
{
std::cout << "T is int." << std::endl;
}
template<typename U = T>
typename std::enable_if<std::is_same<U, float>::value>::type
MyFunction()
{
std::cout << "T is not int." << std::endl;
}
};
Редактировать:
Как упоминает HostileFork в комментариях, исходный пример оставляет пользователю возможность явно указать аргументы шаблона для функций-членов и получить неверный результат. Следующее должно предотвратить явную специализацию функций-членов от компиляции.
template<typename T>
struct Point
{
template<typename... Dummy, typename U = T>
typename std::enable_if<std::is_same<U, int>::value>::type
MyFunction()
{
static_assert(sizeof...(Dummy)==0, "Do not specify template arguments!");
std::cout << "T is int." << std::endl;
}
template<typename... Dummy, typename U = T>
typename std::enable_if<std::is_same<U, float>::value>::type
MyFunction()
{
static_assert(sizeof...(Dummy)==0, "Do not specify template arguments!");
std::cout << "T is not int." << std::endl;
}
};
Простое решение заключается в использовании делегирования работнику частный функции:
template<typename T>
struct Point
{
void MyFunction()
{
worker(static_cast<T*>(0)); //pass null argument of type T*
}
private:
void worker(int*)
{
std::cout << "T is int." << std::endl;
}
template<typename U>
void worker(U*)
{
std::cout << "T is not int." << std::endl;
}
};
когда T
является int
, первый worker
функция будет вызвана, потому что static_cast<T*>(0)
оказывается типа int*
, Во всех остальных случаях будет вызываться шаблонная версия работника.
enable_if
работает только для счислимый аргументы шаблона функции или для специализированных аргументов шаблона класса. То, что вы делаете, не работает, потому что, очевидно, с фиксированным T = int
Второе объявление просто ошибочно.
Вот как это можно сделать:
template <typename T>
void MyFreeFunction(Point<T> const & p,
typename std::enable_if<std::is_same<T, int>::value>::type * = NULL)
{
std::cout << "T is int" << std::endl;
}
// etc.
int main()
{
Point<int> ip;
MyFreeFunction(ip);
}
Альтернативой было бы специализироваться Point
для разных типов T
или поместить вышеупомянутую свободную функцию в оболочку шаблона вложенного элемента (что, вероятно, является более «правильным» решением).
Основываясь на предложении Преториана (но без изменения типа возвращаемого значения функции), это похоже на работу:
#include <iostream>
#include <type_traits>
template<typename T>
struct Point
{
template<typename U = T>
void MyFunction(typename std::enable_if<std::is_same<U, int>::value, U >::type* = 0)
{
std::cout << "T is int." << std::endl;
}
template<typename U = T>
void MyFunction(typename std::enable_if<!std::is_same<U, int>::value, float >::type* = 0)
{
std::cout << "T is not int." << std::endl;
}
};
int main()
{
Point<int> intPoint;
intPoint.MyFunction();
Point<float> floatPoint;
floatPoint.MyFunction();
}
Точечный шаблон, приведенный ниже, можно создать только с int или float в качестве аргумента шаблона T.
Чтобы ответить на вопрос: здесь worker () вызывается точно в зависимости от параметра шаблона вызова метода (), но все же вы управляете типами.
template<typename T>
struct Point
{
static_assert (
std::is_same<T, int>() ||
std::is_same<T, float>()
);
template<typename U>
void method(U x_, U y_)
{
if constexpr (std::is_same<T, U>()) {
worker(x_, y_);
return;
}
// else
worker(
static_cast<T>(x_),
static_cast<T>(y_)
);
return ;
}private:
mutable T x{}, y{};
void worker(T x_, T y_)
{
// nothing but T x, T y
}
};
Выше работник (), конечно, будет работать, даже если он объявлен как статический. По какой-то уважительной причине. Несколько других расширений к вышеупомянутому возможны (и просты), но давайте придерживаться ответа.