Я писал что-то, чтобы использовать SFINAE, чтобы не генерировать функцию при определенных условиях. Когда я использую метакод напрямую, он работает так, как ожидалось, но когда я использую код косвенно через другой класс, он не работает должным образом.
Я думал, что это было связано с VC++, но похоже, что g++ также имеет это, поэтому мне интересно, есть ли какая-то причина, по которой SFINAE не применяется к этому случаю.
Код прост. Если используемый класс не является базовым классом «класса коллекции», не создавайте функцию.
#include <algorithm>
#include <type_traits>
#define USE_DIRECT 0
#define ENABLE 1
class A{};
class B{};
class C{};
class D{};
class collection1 : A, B, C {};
class collection2 : D {};
#if USE_DIRECT
template<typename X>
typename std::enable_if<std::is_base_of<X, collection1>::value, X>::type fn(X x)
{
return X();
}
# if ENABLE
template<typename X>
typename std::enable_if<std::is_base_of<X, collection2>::value, X>::type fn(X x)
{
return X();
}
# endif
#else // USE_DIRECT
template<typename X, typename COLLECTION>
struct enable_if_is_base_of
{
static const int value = std::is_base_of<X, COLLECTION>::value;
typedef typename std::enable_if<value, X>::type type;
};
template<typename X>
typename enable_if_is_base_of<X, collection1>::type fn(X x)
{
return X();
}
# if ENABLE
template<typename X>
typename enable_if_is_base_of<X, collection2>::type fn(X x)
{
return X();
}
# endif
#endif // USE_DIRECT
int main()
{
fn(A());
fn(B());
fn(C());
fn(D());
return 0;
}
Если я установлю для USE_DIRECT значение 1, а для ENABLE значение 0, компиляция не удастся, так как нет функции fn
, которая принимает параметр D
. Установка ENABLE в 1 предотвратит появление этой ошибки.
Однако, если я установлю USE_DIRECT на 0 и ENABLE на 0, произойдет сбой с другими сообщениями об ошибках, но в том же случае нет fn
, который принимает параметр D
. Однако установка ENABLE в 1 приведет к сбою всех 4 вызовов функций.
Для вашего удобства вот код в онлайн-компиляторе: http://goo.gl/CQcXHr
Может кто-нибудь объяснить, что здесь происходит и почему?
Похоже, это может быть связано с шаблонами псевдонимов, используемыми в SFINAE приводит к серьезной ошибке, но и на это никто не ответил.
Для справки, вот ошибки, которые были сгенерированы g++:
main.cpp: In instantiation of 'struct enable_if_is_base_of<A, collection2>':
main.cpp:45:53: required by substitution of 'template<class X> typename enable_if_is_base_of<X, collection2>::type fn(X) [with X = A]'
main.cpp:54:8: required from here
main.cpp:34:82: error: no type named 'type' in 'struct std::enable_if<false, A>'
typedef typename std::enable_if<std::is_base_of<X, COLLECTION>::value, X>::type type;
^
main.cpp: In instantiation of 'struct enable_if_is_base_of<B, collection2>':
main.cpp:45:53: required by substitution of 'template<class X> typename enable_if_is_base_of<X, collection2>::type fn(X) [with X = B]'
main.cpp:55:8: required from here
main.cpp:34:82: error: no type named 'type' in 'struct std::enable_if<false, B>'
main.cpp: In instantiation of 'struct enable_if_is_base_of<C, collection2>':
main.cpp:45:53: required by substitution of 'template<class X> typename enable_if_is_base_of<X, collection2>::type fn(X) [with X = C]'
main.cpp:56:8: required from here
main.cpp:34:82: error: no type named 'type' in 'struct std::enable_if<false, C>'
main.cpp: In instantiation of 'struct enable_if_is_base_of<D, collection1>':
main.cpp:38:53: required by substitution of 'template<class X> typename enable_if_is_base_of<X, collection1>::type fn(X) [with X = D]'
main.cpp:57:8: required from here
main.cpp:34:82: error: no type named 'type' in 'struct std::enable_if<false, D>'