私はこれを本質にまで煮詰めようとしました。タイプでインデックス付けされたオブジェクトの「リスト」を含む可変個引数テンプレートクラスFooがあります。この関数を使用してbar<U>()
、そのタイプの要素を抽出します。可変個引数テンプレートとstd :: enable_ifを使用してこれを解決し、bar<U>()
T == Uの場所のみを定義します。次に、「using」を使用して基本クラスのすべての「bar」関数を公開します。
#include <type_traits>
template<typename... Ts>
class Foo
{
public:
void bar() {}
};
template<typename T, typename... Ts>
class Foo<T, Ts...> : public Foo<Ts...>
{
public:
using Foo<Ts...>::bar;
template<typename U>
typename std::enable_if<std::is_same<U, T>::value, U >::type
bar()
{
return mObj;
}
private:
T mObj;
};
template<typename T>
void bar2()
{
Foo<int, float, double> list;
list.bar<T>();
}
int main()
{
bar2<float>();
return 0;
}
これは、ClangとVisual Studio 2015を除いて、うまく機能します。MSVC19.0と19.10の両方を試したところ、次のエラーが発生しました。
Compiled with /EHsc /nologo /W4 /c
main.cpp
main.cpp(30): error C2672: 'Foo<int,float,double>::bar': no matching overloaded function found
main.cpp(35): note: see reference to function template instantiation 'void bar2<float>(void)' being compiled
main.cpp(30): error C2770: invalid explicit template argument(s) for 'std::enable_if<std::is_same<U,T>::value,U>::type Foo<int,float,double>::bar(void)'
with
[
T=int
]
main.cpp(18): note: see declaration of 'Foo<int,float,double>::bar'
少なくとも4.7から6.3の間のGCCは、これをうまくコンパイルします。これは、Visual Studio2015にないc ++ 11の一部の機能によるものかもしれないと最初に思いましたが、驚くべきことに、これは古いVisual Studio 2013(MSVC 18.0)では正常にコンパイルされます。Clangも失敗します。
だから私の質問は、これはこれらのコンパイラの欠点ですか、それとも私がここで行っている許可されていないことはありますか?
list.bar<int>()
テストされたすべてのコンパイラでコンパイルされるようなハードコードされたタイプで「bar」を呼び出すと、
enable_if
ここで使用するには、whenの代替オプションを提供する必要がありますis_same<U, T>::value == false
。理想的には、これは、すべての基本クラスメンバーbar
をusing宣言で公開することによって実行できます...
using Foo<Ts...>::template bar;
残念ながら、これは規格で禁止されており、これを修正しないことが決定されました。したがって、別の方法でそれらを公開する必要があります。したがって、最も簡単な解決策はFoo<Ts...>::template bar()
、次のようにのラッパーを作成することです。
template<typename T, typename... Ts>
class Foo<T, Ts...> : public Foo<Ts...>
{
public:
// using Foo<Ts...>::template bar; // Sadly, this is forbidden.
template<typename U>
typename std::enable_if<std::is_same<U, T>::value, U >::type
bar()
{
return mObj;
}
// Additional wrapper, to compensate for lack of valid syntax.
template<typename U>
typename std::enable_if<!std::is_same<U, T>::value, U >::type
bar()
{
return Foo<Ts...>::template bar<U>();
}
private:
T mObj;
};
ただし、ラッパーは。をFoo<Ts...>::bar()
返すため、を呼び出すことができないことに注意してくださいvoid
。U
がパックのメンバーではない場合に使用することを目的とした一般的なケースであると仮定すると、これを修正する方法は2つあります。
変更しFoo<Ts...>::bar()
ます。
template<typename... Ts>
class Foo
{
public:
template<typename T>
T bar()
{
// Return an invalid value.
return T{-1};
}
};
のメンバーでないFoo<T, Ts...>::bar()
場合に使用する、の3番目のバージョンを提供します。これはを返します。このためには、特性を定義して、それがパックに含まれているかどうかを検出すると便利です。U
T, Ts...
Foo<Ts...>::bar()
template<typename...>
struct is_in_pack : std::false_type {};
template<typename U, typename T1, typename... Ts>
struct is_in_pack<U, T1, Ts...> :
std::integral_constant<bool,
std::is_same<U, T1>::value ||
is_in_pack<U, Ts...>::value>
{};
次に、トレイトを使用する必要があります。
template<typename T, typename... Ts>
class Foo<T, Ts...> : public Foo<Ts...>
{
public:
// using Foo<Ts...>::template bar; // Sadly, this is forbidden.
template<typename U>
typename std::enable_if<std::is_same<U, T>::value, U >::type
bar()
{
return mObj;
}
// Additional wrapper, to compensate for lack of valid syntax.
// U is a member of <T, Ts...>.
template<typename U>
typename std::enable_if<!std::is_same<U, T>::value &&
is_in_pack<U, T, Ts...>::value, U >::type
bar()
{
return Foo<Ts...>::template bar<U>();
}
// Additional wrapper, to compensate for lack of valid syntax.
// U isn't a member of <T, Ts...>.
template<typename U>
typename std::enable_if<!is_in_pack<U, T, Ts...>::value>::type
bar()
{
return Foo<>::bar();
}
private:
T mObj;
};
これらのオプションのうち、現在のコードにより近いので、後者をお勧めします。
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加