Вот MWE того, как этого можно достичь:
#include <iostream>
using namespace std;
struct multi;
struct single;
template<class T, class Q>
struct dummy {
dummy(T value) : m_value(value) { }
// using enable_if_t in template argument
template<class _Q = Q, enable_if_t<is_same<_Q, single>::value && is_same<_Q, Q>::value, int> = 1>
T increment() { return ++m_value; }
// using enable_if_t in method return type
template<class _Q = Q>
enable_if_t<is_same<_Q, multi>::value && is_same<_Q, Q>::value, T>
//enable_if_t<is_same<_Q, multi>::value, T> // (*)
increment(T delta) { return (m_value += delta); }
T m_value;
};
int main() {
dummy<double, multi> m(47.10);
//cout << m.increment() << endl; // error as expected
cout << m.increment(.01) << endl;
dummy<int, single> s(41);
cout << s.increment() << endl;
cout << s.increment<single>() << endl;
//cout << s.increment(1) << endl; // error as expected
//cout << s.increment<multi>(1) << endl; // not an error when using (*)
}
выход
Использование c++ (Debian 6.2.1-5) 6.2.1 20161124
дает:
47.11
42
43
Разработка
Нам нужно шаблонизировать методы, чтобы заставить SFINAE работать вообще. Мы не можем использовать что-то вроде
std::enable_if_t<std::is_same<_Q, multiple_increment>::value, T> increment() ...
потому что происходит сбой при создании шаблона dummy<T, single_increment>
вместо сбоя при замене параметров шаблона метода.
Кроме того, мы хотим, чтобы пользователь мог использовать методы без фактического предоставления параметра шаблона метода. Поэтому мы устанавливаем параметр шаблона метода _Q
по умолчанию Q
.
Наконец, чтобы действительно вызвать ошибку компилятора при использовании нежелательного метода, даже при предоставлении параметра шаблона метода, мы только enable_if_t
используем метод, только если параметр шаблона метода _Q
действительно того же типа, что и соответствующий параметр шаблона класса Q
.