enable_if
あるクラス テンプレートで定義されたイネーブラー ( のエイリアス テンプレート) を別のクラス テンプレートで使用したいと考えています。次のようになります。
template< ... > using enabler = typename std::enable_if< ... >::type;
これは SFINAE では問題なく機能します。しかし、次のような 2 番目のクラスに別のエイリアス テンプレートを追加すると、
template< ... > using enabler
= typename first_class<..> :: template enabler< ... >;
このイネーブラーを SFINAE に使用すると、置換は (正しく) 失敗しますが、g++4.8.0 および 4.8.1 ではハード エラーが発生します。clang++3.4 ではソフト エラーのみが発生し、SFINAE は機能します。
#include <iostream>
#include <type_traits>
#include <cstdlib>
template< typename T >
class A
{
struct B {};
public :
struct U : B {};
template< typename D, typename R = void >
using enable_if_is_B = typename std::enable_if< std::is_base_of< B, D >::value, R >::type;
template< typename D, typename R = void >
using enable_if_is_not_B = typename std::enable_if< !std::is_base_of< B, D >::value, R >::type;
template< typename D >
auto
operator () (D const &) const
-> enable_if_is_B< D >
{
std::cout << "D is B" << std::endl;
}
template< typename D >
auto
operator () (D const &) const
-> enable_if_is_not_B< D >
{
std::cout << "D is not B" << std::endl;
}
};
template< typename T >
struct B
{
using A_type = A< T >;
template< typename D, typename R = void >
using enable_if_is_B = typename A_type::template enable_if_is_B< D, R >;
template< typename D, typename R = void >
using enable_if_is_not_B = typename A_type::template enable_if_is_not_B< D, R >;
template< typename D >
auto
operator () (D const &) const
-> enable_if_is_B< D >
{
std::cout << "D is B!" << std::endl;
}
template< typename D >
auto
operator () (D const &) const
-> enable_if_is_not_B< D >
{
std::cout << "D is not B!" << std::endl;
}
};
int main()
{
using T = struct Z;
using A_type = A< T >;
using U = typename A_type::U;
#if 0
using X = A< T >;
#else
using X = B< T >;
#endif
X const x;
x(0);
x(U());
return EXIT_SUCCESS;
}
どちらが#if 1
生成されますか:
D is not B
D is B
しかし、using X = B< T >;
g+4.8.0 でエラーが発生します。
<stdin>: In substitution of 'template<class T> template<class D, class R> using enable_if_is_B = typename std::enable_if<std::is_base_of<A<T>::B, D>::value, R>::type [with D = int; R = void; T = main()::Z]':
<stdin>:48:73: required by substitution of 'template<class T> template<class D, class R> using enable_if_is_B = typename B<T>::A_type::enable_if_is_B<D, R> [with D = int; R = void; T = main()::Z]'
<stdin>:55:2: required by substitution of 'template<class D> B<T>::enable_if_is_B<D> B<T>::operator()(const D&) const [with D = D; T = main()::Z] [with D = int]'
<stdin>:82:5: required from here
<stdin>:18:91: error: no type named 'type' in 'struct std::enable_if<false, void>'
<stdin>: In substitution of 'template<class T> template<class D, class R> using enable_if_is_not_B = typename std::enable_if<(! std::is_base_of<A<T>::B, D>::value), R>::type [with D = A<main()::Z>::U; R = void; T = main()::Z]':
<stdin>:51:81: required by substitution of 'template<class T> template<class D, class R> using enable_if_is_not_B = typename B<T>::A_type::enable_if_is_not_B<D, R> [with D = A<main()::Z>::U; R = void; T = main()::Z]'
<stdin>:63:2: required by substitution of 'template<class D> B<T>::enable_if_is_not_B<D> B<T>::operator()(const D&) const [with D = D; T = main()::Z] [with D = A<main()::Z>::U]'
<stdin>:83:7: required from here
<stdin>:21:96: error: no type named 'type' in 'struct std::enable_if<false, void>'
あるクラス テンプレートから別のクラス テンプレートにイネーブラーを「エクスポート/インポート」する方法は?
SFINAE が機能しないのはなぜですか?