«использование» декларации в качестве SFINAE

Могу ли я использовать SFINAE (или другую технику) для using объявление в то время как личное наследование от шаблона класса?
Для лучшего понимания смотрите код ниже:

#include <iostream>

struct S1 {
void f() { std::cout << "S1::f\n"; }
};

struct S2 {
void f() { std::cout << "S2::f\n"; }
void g() { std::cout << "S2::g\n"; }
};

template <class T>
struct D : private T {
using T::f;
// using T::g; // need this only if T provides g() function
};

int main() {
D<S1>().f(); // ok. Prints 'S1::f'
D<S2>().f(); // ok. Prints 'S2::f'
D<S2>().g(); // fail. But wants to be ok and prints 'S2::g'
return 0;
}

Как я могу достичь желаемого поведения (если это возможно)?

7

Решение

C ++ частичная специализация и использование шаблонов decltype(void(&T::g)) для SFINAE

#include <iostream>
#include <type_traits>

struct S1 {
void f() { std::cout << "S1::f\n"; }
};

struct S2 {
void f() { std::cout << "S2::f\n"; }
void g() { std::cout << "S2::g\n"; }
};

template <class T, class V = void>
struct D : private T {
using T::f;
};

template <class T>
struct D<T, decltype(void(&T::g))> : private T {
using T::f;
using T::g; // need this only if T provides g() function
};

int main() {
D<S1>().f(); // ok. Prints 'S1::f'
D<S2>().f(); // ok. Prints 'S2::f'
D<S2>().g(); // ok. Prints 'S2::g'
return 0;
}

Live Demo


Редактировать:

Это еще один подход, который является более гибким, но я понятия не имею, как private virtual Наследование работает с реальными вариантами использования. Пожалуйста, дайте мне знать, если это может вызвать какие-либо проблемы (например, UB).

#include <iostream>
#include <type_traits>

struct S1 {
void f() { std::cout << "S1::f\n"; }
};

struct S2 {
void f() { std::cout << "S2::f\n"; }
void g() { std::cout << "S2::g\n"; }
};

struct S3 {
void g() { std::cout << "S3::g\n"; }
};

template <class T, class = void>
struct D_f {};

template <class T>
struct D_f<T, decltype(void(&T::f))> : private virtual T {
using T::f;
};

template <class T, class = void>
struct D_g {};

template <class T>
struct D_g<T, decltype(void(&T::g))> : private virtual T {
using T::g;
};

template <class T>
struct D : D_f<T>, D_g<T> {
};int main() {
D<S1>().f();
D<S2>().f();
D<S2>().g();
D<S3>().g();
return 0;
}

Live Demo

4

Другие решения

Вариант ответа Брайана Чена, который выглядит уродливее, но облегчает расширение до нескольких проверок и не требует дублирования кода, который разделяется между D<type-with-f> а также D<type-without-f>, должен использовать цепочку наследования, где каждый шаг проверяет одного дополнительного члена. Единственное необходимое дублирование — это наследование конструкторов, если это уместно.

struct A {
void f() { }
void g() { }
void i() { }
};

// The generic case. D<T, char[N]> simply provides what D<T, char[N+1]> provides.
template <typename T, typename U = char[1]>
struct D : D<T, char[sizeof(U) + 1]> {
using D<T, char[sizeof(U) + 1]>::D;
};

// The end of the chain. This is where T gets inherited. It declares all of its own
// specialisations as its friends, so that they can access other members of T.
template <typename T>
struct D<T, char[6]> : private T {
template <typename, typename>
friend struct D;

D(int) { }
void fun() { }
};

// Check for T::f.
template <typename T>
struct D<T, char[2 + !sizeof(&T::f)]> : D<T, char[3]> {
using D<T, char[3]>::D;
using T::f;
};

// Check for T::g.
template <typename T>
struct D<T, char[3 + !sizeof(&T::g)]> : D<T, char[4]> {
using D<T, char[4]>::D;
using T::g;
};

// Check for T::h.
template <typename T>
struct D<T, char[4 + !sizeof(&T::h)]> : D<T, char[5]> {
using D<T, char[5]>::D;
using T::h;
};

// Check for T::i.
template <typename T>
struct D<T, char[5 + !sizeof(&T::i)]> : D<T, char[6]> {
using D<T, char[6]>::D;
using T::i;
};

int main() {
D<A> d = 4; // ok: verify that constructors got inherited
// A &a = d; // error: verify that inheritance of A is private
d.f(); // ok: verify that f got inherited
d.g(); // ok: verify that g got inherited
// d.h(); // error: verify that h is not available
d.i(); // ok: verify that i got inherited
d.fun(); // ok: verify that the inheritance chain didn't get broken
}

Примечание: вместо проверки &T::fвы можете сделать что-то с std::declval<T>().f() вместо. Первый не может обрабатывать перегруженные функции.

5

По вопросам рекламы [email protected]