Предположим, у нас есть что-то вроде этого:
template <class B>
class A
{
void Foo(B& b)
{
b.Bar(0.5);
}
};
class B
{
void Bar(float) {}
void Bar(double) {}
void Bar(int) {}
};
В этом коде введите B
должен предоставить метод Bar()
который принимает аргумент некоторого целочисленного типа. Проблема здесь во всех 3 версиях B::Bar()
разрешены. Есть ли способ разрешить только одну версию этих методов, например, только компилировать, если B
обеспечивает Bar(float)
?
Вы можете использовать эту (ужасную) технику, которая приведет к сбою компиляции, если A
создается с типом B
это не имеет общественности void Foo(float)
член, пытаясь извлечь из него определенный тип указателя на член.
template <class B>
class A
{
public:
void Foo(B& b)
{
static_cast<void (B::*)(float)>(&B::Bar);
b.Bar(0.5);
}
};
(Демонстрация полученной ошибки компиляции.)
Если вы действительно хотите вызвать этот метод, тогда вам нужно использовать b.Bar(0.5f);
, 0.5
это double
буквальный, а не float
буквально, так что вы должны убедиться, что у него есть правильный член, но затем, если у него есть void Bar(double)
Вы бы назвали это так или иначе. Изменение константы на 0.5f
бы исправить это.
Обратите внимание, что поскольку взятие указателя не имеет побочных эффектов и результат не используется, любой приличный компилятор оптимизирует это как неиспользуемое.
Вы также можете пойти по традиционному маршруту SFINAE примерно так:
template <typename T, typename TMethod>
class has_bar_method
{
private:
struct yes { char _; };
struct no { char _[2]; };
template <typename U, TMethod = &U::Bar>
static yes impl(U*);
static no impl(...);
public:
enum { value = sizeof(impl(static_cast<T*>(nullptr))) == sizeof(yes) };
};
Используется так:
void Foo(T& b)
{
static_assert(has_bar_method<T, void (T::*)(float)>::value,
"T has method void Bar(float)");
b.Bar(0.5f);
}
Теперь, если шаблон не может быть создан, мы получаем хорошее сообщение, объясняющее почему:
prog.cpp: 25: 8: ошибка: статическое утверждение не выполнено: T имеет метод void Bar (float)