Я предлагаю следующее X
struct
template <typename T>
struct X
{
static void g (...)
{ std::cout << "Operation not supported!" << std::endl; }
template <typename ... As, typename U = T>
static auto g(int, As && ... as)
-> decltype( U::f(std::forward<As>(as)...) )
{ return T::f(std::forward<As>(as)...); }
template <typename ... As>
static auto f (As && ... as)
{ return g(0, std::forward<As>(as)...); }
};
Точки:
(1) Проход через функцию g()
с дополнительным неиспользуемым параметром (int
вВариантная версия, адсорбированная ...
в «не поддерживаемой версии»), позволяет выбрать вариационную версию (называемую 0
, то есть int
), когда доступны функции sizeof...(As) == 0
и обе g()
.Это потому, что int
является лучшим соответствием (в точном соответствии) для int
как ...
.Но когда версия с переменным числом недоступна (см. Пункт (2)), «не поддерживаемая» версия все еще доступна (доступна только одна) и выбрана
(2) Необходимо включить / отключить SFINAE.Вариативная версия в соответствии с тем, что статический метод f()
доступен (или не доступен) в T
.К сожалению, T
является аргументом шаблона класса, а не статического метода g()
, поэтому вы можете использовать его для SFINAE.Хитрость заключается в том, что SFINAE работает с дополнительным параметром шаблона, U
, который по умолчанию равен T
// ------------------------VVVVVVVVVVVVVV additional U (defaulted to T) template type
template <typename ... As, typename U = T>
static auto g(int, As && ... as)
-> decltype( U::f(std::forward<As>(as)...) )
// -------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//
// SFINAE enable this g() if (and only if) the call
// U::f(std::forward<As>(as)...) is valid
Ниже приведен упрощенный рабочий пример
#include <iostream>
#include <type_traits>
struct A
{ static void f() { std::cout << "A::f()" << std::endl; } };
struct B
{
static size_t f(std::string const& s_)
{ std::cout << "B::f()" << std::endl; return s_.size(); }
};
struct C
{ };
template <typename T>
struct X
{
static void g (...)
{ std::cout << "Operation not supported!" << std::endl; }
template <typename ... As, typename U = T>
static auto g(int, As && ... as)
-> decltype( U::f(std::forward<As>(as)...) )
{ return T::f(std::forward<As>(as)...); }
template <typename ... As>
static auto f (As && ... as)
{ return g(0, std::forward<As>(as)...); }
};
int main ()
{
X<A>::f(); // now call variadic version
auto const x = X<B>::f("Hello"); // Works just fine as before
std::cout << x << std::endl;
X<C>::f(); // now compile
}