3

私はクラスFooとクラスBarを持っています。それらはstd :: array上のラッパーです。 どちらも派生クラスをいくつか持っています。テンプレートパラメータの制限付きでいくつかの外部テンプレートクラスの部分的な特殊化を定義します

template<typename T, std::size_t N> 
struct Foo { 
    std::array<T, N> data; 
}; 

template<typename T, std::size_t N> 
struct FooDerived : Foo <T, N> {}; 

template<typename T, std::size_t N> 
struct Bar { 
    std::array<T, N> data; 
}; 

template<typename T, std::size_t N> 
struct BarDerived : Bar <T, N> {}; 

そして、私はstd名前空間にタプルインターフェイスを実装します:GET/tuple_size/tuple_element。しかし、これらのメソッドはFooのためにしか利用できず、Fooクラスから派生しているはずです。

namespace std { 
template<template<typename, std::size_t> class T, typename TArg, std::size_t NArg> 
class tuple_size<T<TArg, NArg>> 
    : public integral_constant<std::size_t, NArg> 
    { 
    }; 

template<std::size_t I, template<typename, std::size_t> class T, typename TArg, std::size_t NArg> 
struct tuple_element<I, T<TArg, NArg>> 
    { 
    using type = TArg; 
    }; 
} // namespace std 

これは機能しますが、バーとバー由来のクラスでも機能します。

私はstd :: enable_ifとstd :: is_base_ofを使用すると考えました。クラスのstd :: enable_ifをテンプレートパラメータとして使用できます。 そして、私が書く場合:

template<template<typename, std::size_t> class T, typename TArg, std::size_t NArg, 
    typename std::enable_if<std::is_base_of<Foo<TArg, NArg>, T<TArg, NArg>>::value, int>::type = 0> 
class tuple_size<T<TArg, NArg>> 
    : public integral_constant<std::size_t, NArg> 
    { 
    }; 

それはコンパイルエラーにつながる:デフォルトテンプレート引数は、部分的な特殊化で使用することはできません。

Fooクラスと無関係にタプルのようなインタフェースを使用することは禁じられていますか?

例:http://rextester.com/JEXJ27486

更新: は、私は解決策を見つけるようです。 static_assertより柔軟性があります。 部分的な特殊化でデフォルトのテンプレート引数を使用できない場合は、完全なテンプレートの特殊化を使用してクラスを追加します。

template<typename T, typename TArg, std::size_t NArg, typename = void> 
struct tuple_resolver; 

template<typename T, typename TArg, std::size_t NArg> 
struct tuple_resolver<T, TArg, NArg, 
    typename std::enable_if<std::is_base_of<Foo<TArg, NArg>, T>::value>::type> 
    : public integral_constant<std::size_t, NArg> 
{ 
    using type = TArg; 
}; 

template<template<typename, std::size_t> class T, 
    typename TArg, 
    std::size_t NArg> 
class tuple_size<T<TArg, NArg>> 
    : public tuple_resolver<T<TArg, NArg>, TArg, NArg> 
    { 
    }; 

例:http://rextester.com/KTDXNJ90374

+0

私は何 '型名STDわかりません。 :enable_if <...> :: type = 0'は、テンプレートパラメータの宣言としても意味されます。通常の 'typename = std :: enable_if <...> :: type'構文を使用するとどうなりますか? –

+0

Daniel Schepler、私は第4の例からこの構文をとった:[cppreference](http://en.cppreference.com/w/cpp/types/enable_if)。しかし、問題は構文ではありません。'typename = std :: enable_if :: type'でもコンパイルできません。というのも、エラーが示すように、デフォルトのテンプレート引数は部分的な特殊化では使用できないからです。 – Vladislav

+1

ええ、実際には 'typename std :: enable_if :: type = 0'を使っていますが、ちょっとラウンドアバウトであっても意味があります。 –

答えて

3

あなたが提案したが、まだ標準化された言語機能を使用してOKであれば、これは-fconceptsフラグではgcc 6.1の下で動作しているようです:

template <typename Base, typename Derived> 
concept bool BaseOf = std::is_base_of<Base, Derived>::value; 

namespace std { 
    template <template<typename,std::size_t> class Tmpl, typename T, std::size_t N> 
    requires BaseOf<Foo<T, N>, Tmpl<T, N>> 
    class tuple_size<Tmpl<T, N>> 
    : public std::integral_constant<std::size_t, N> 
    { }; 
}; 

// tests 
static_assert(std::tuple_size<FooDerived<int, 5>>::value == 5, 
       "FooDerived"); 
static_assert(std::tuple_size<std::array<int, 5>>::value == 5, 
       "std::array"); 

template <typename T> 
concept bool SizedTuple = requires { 
    { std::tuple_size<T>::value } -> std::size_t 
}; 
static_assert(!SizedTuple<BarDerived<int, 5>>, "BarDerived"); 
+0

うわー。このソリューションは素晴らしく見えます。たぶん、数年後に私はそれを使用します) – Vladislav

2

何古き良きstatic_assert()はどうですか?

#include <array> 
#include <iostream> 
#include <type_traits> 

template<typename T, std::size_t N> 
struct Foo { 
    std::array<T, N> data; 
}; 

template<typename T, std::size_t N> 
struct FooDerived : Foo <T, N> {}; 

template<typename T, std::size_t N> 
struct Bar { 
    std::array<T, N> data; 
}; 

template<typename T, std::size_t N> 
struct BarDerived : Bar <T, N> {}; 

template <typename> 
class tuple_size; 


template <template <typename, std::size_t> class T, 
      typename TArg, 
      std::size_t NArg> 
class tuple_size<T<TArg, NArg>> 
{ 
    static_assert(std::is_base_of<Foo<TArg, NArg>, T<TArg, NArg>>::value, 
       "bad tuple_size class"); 
}; 


int main() 
{ 
    tuple_size<Foo<int, 12>>   ts1; // compile 
    tuple_size<FooDerived<long, 21>> ts2; // compile 
    //tuple_size<Bar<int, 11>>   ts3; // error! 
    //tuple_size<BarDerived<long, 22>> ts4; // error! 
} 
+1

私が知る限り、失敗した 'static_assert'はハードエラーであり、置換失敗ではありません。したがって、 'std :: tuple_size >'を後で使用した場合、OPが望んでいるように、そのテンプレートに対してテンプレートを許可しないだけで、テンプレートにコンパイラエラーが発生します。 –

+0

@DanielSchepler - はい、代用エラーではありません。それは本当に問題ですか?置換失敗またはハードエラーは、コンパイラエラーでもあります(または置換エラーはリンクエラーですか、私は混乱します)。また、インターフェイスの使用を禁止します。 – max66

+0

なぜ私はそれについて考えなかったのかわかりません。それは動作し、私の場合には適切な解決策と思われます。しかし、ちょうど私がtuple_size を許可し、別のサイズ、例えばN + 1を返すことを決めたらどうなるでしょうか? – Vladislav

0

あなたは別の派生グループの種類ごとに異なるtuple_sizeのクラスをしたい場合は、(static_assert()に基づいて)私の先例ソリューションは、仕事をdoes't。

Iはトリッキー(および醜い、私は仮定)部分が可能であれば、型指定された、(FooBar間)ベースの検出、structbaseTypeあるstd::conditionalstd::is_base_of

に基づいて、以下の解決策を提案します。

#include <array> 
#include <iostream> 
#include <type_traits> 

template<typename T, std::size_t N> 
struct Foo { 
    std::array<T, N> data; 
}; 

template<typename T, std::size_t N> 
struct FooDerived : Foo <T, N> {}; 

template<typename T, std::size_t N> 
struct Bar { 
    std::array<T, N> data; 
}; 

template<typename T, std::size_t N> 
struct BarDerived : Bar <T, N> {}; 


template <typename> 
struct baseType; 

template <template <typename, std::size_t> class T, 
      typename TArg, 
      std::size_t NArg> 
struct baseType<T<TArg, NArg>> 
{ 
    using derT = T<TArg, NArg>; 
    using bas1 = Foo<TArg, NArg>; 
    using bas2 = Bar<TArg, NArg>; 
    using type = typename std::conditional< 
        std::is_base_of<bas1, derT>::value, bas1, 
         typename std::conditional< 
         std::is_base_of<bas2, derT>::value, bas2, 
          void>::type>::type; 
}; 

template <typename T, typename = typename baseType<T>::type> 
class tuple_size; 

template <template <typename, std::size_t> class T, 
      typename TArg, 
      std::size_t NArg> 
class tuple_size<T<TArg, NArg>, Foo<TArg, NArg>> 
{ public: static constexpr std::size_t size {NArg}; }; 

template <template <typename, std::size_t> class T, 
      typename TArg, 
      std::size_t NArg> 
class tuple_size<T<TArg, NArg>, Bar<TArg, NArg>> 
{ public: static constexpr std::size_t size { NArg << 1 }; }; 

int main() 
{ 
    std::cout << "size of Foo<int, 12>:   " 
     << tuple_size<Foo<int, 12>>::size << std::endl; //   print 12 
    std::cout << "size of FooDerived<long, 11>: " 
     << tuple_size<FooDerived<long, 11>>::size << std::endl; // print 11 
    std::cout << "size of Bar<int, 12>:   " 
     << tuple_size<Bar<int, 12>>::size << std::endl; //   print 24 
    std::cout << "size of BarDerived<long, 11>: " 
     << tuple_size<BarDerived<long, 11>>::size << std::endl; // print 22 
    //std::cout << "size of std::array<long, 10>: "    // compiler 
    // << tuple_size<std::array<long, 10>>::size << std::endl; // error 
} 
+0

それは私が欲しかったものではありません。私は、単にstd :: tuple_sizeのための別の特殊化を追加することを意味しました。これは、テンプレートクラスが標準の配列、タプル、およびクラス(FooとBarから派生したもの)で動作できるように、このメソッドを別のテンプレートクラスのインターフェイスのように使用できるようにします。あなたの提案では、別のtuple_sizeを使用しました(std名前空間からではありません)。そのソリューションは私の場合には適していません。 – Vladislav

関連する問題