2017-06-13 8 views
2

型の特性を使用して、std::arrayまたは普通の古いCスタイルの配列の要素型を取得したいとします。 std::array<char, 3>またはchar[3]のいずれかを指定すると、charが返されます。これを行うにはstd :: arrayまたはCスタイルの配列の要素型を取得するtrait型

メカニズムは、私は、プレーンアレイ上std::array::value_type、およびstd::remove_all_extentsを使用することができます...部分的にしか場所であるように見えるが、私は、私は」の両方を兼ね備えたシングルタイプの形質を見つけることができません自分で書くことができません。

私は、この限りで持っている:

#include <array> 
#include <type_traits> 

template <class T> 
using element_type = typename std::conditional< 
    std::is_array<T>::value, 
    typename std::remove_all_extents<T>::type, 
    typename T::value_type 
>::type; 

もちろん、std::arrayのためだけで正常に動作:

int main() 
{ 
    static_assert(
     std::is_same<char, element_type<std::array<char, 3>>>::value, 
     "element_type failed"); 
} 

が、休憩が、私はそれをプレーンな配列を渡し、明らかに平野理由配列には::value_typeが含まれていません。 、あなたが期待するよう:「に続いたときに 『::』をクラスまたは名前空間でなければならない『T』」

static_assert(std::is_same<char, element_type<char[3]>>::value, "element_type failed"); 

だけのようなエラーが発生します。

私が関数を書いていたのであれば、std::enable_ifを使って問題のテンプレートのインスタンス化を隠していましたが、このアプローチを型の特性でどのように使うことができません。

この問題を解決する正しい方法は何ですか?

答えて

5

template<typename T> 
using element_type_t = std::remove_reference_t<decltype(*std::begin(std::declval<T&>()))>; 

std::beginあなたが知っている可能性としては、イテレータを返します。これを省略すると、その値が得られます。decltypeのタイプを取得できます。 std::remove_reference_tは、イテレータが参照している要素への参照を返すので必要です。結果として、これはstd::beginに過負荷があるすべての単一のタイプに対して機能します。

+1

私はあなたが' std :: begin'。 – Rook

+0

少し改善することができます。範囲ベースのforは 'begin/end'関数を見つけるが、このエイリアステンプレートは見つからないコーナーケースがいくつかあります。 'auto begin_adl_helper(T && t){namespace stdを使って関数を定義する; return begin(t);} 'この関数を使用するようにエイリアスを変更します。次に、std以外の名前空間で 'begin'をオーバーロードする型もうまくいきます。 – SirGuy

+0

@SirGuy本当に?私はいつも、使用のために内部的に開始と終了があると考えました。あなたは例を挙げることができますか? – Rakete1111

3

これを行う方法は、(私の知る限りとどちらもすることができ、それを)専門のテンプレート

template<typename> 
struct arr_trait; 

template<typename T, size_t N> 
struct arr_trait<T[N]> {using type = T;}; 

template<typename T, size_t N> 
struct arr_trait<std::array<T, N>> {using type = T;}; 

template<typename T> 
struct arr_trait<T&> : arr_trait<T> {}; 

template<typename T> 
struct arr_trait<T&&> : arr_trait<T> {}; 

template<typename T> 
using element_type = typename arr_trait<T>::type; 

Live

それがサポートされていないため、 std::conditionalが失敗している理由がある

に派遣することです 短絡をサポートし、両方のタイプが評価されます。

+0

私はちょうどhttps://stackoverflow.com/a/22713242/1450890を読んでいました。結論として、あなたが提案したアプローチを使用しなければならないと思います。 _no_型形質が私が望むような短絡を行うことができると言うのは本当ですか? – Rook

+0

@Rook私が知る限り、 –

+0

'foo 'の構文を思い出させてくれてありがとう、ありがとうございました。これは、普通の配列テンプレートのパラメータを扱うもっと良い方法でした。愚かなことです。私は 'unique_ptr'をプレーン配列で使用するためにドキュメントを読んできました。 – Rook

4

std::arrayとCスタイルの配列の両方で呼び出せる関数/演算子は何ですか?当然のoperator[]

template <class Array> 
using array_value_type = decay_t<decltype(std::declval<Array&>()[0])>; 

これは、整数で見上げ、std::map<std::size_t, T>std::vectorを含むサポートされていることを何のために働くなど

あなたは非対constの配列から得るものを区別したい場合あなたはの線に沿って何かをという名前の2種類の特性を作成することがあります-COST配列:

template <class Array> 
using array_element_t = decay_t<decltype(std::declval<Array>()[0])>; 

template <class Array> 
using array_value_t = remove_reference_t<decltype(std::declval<Array>()[0])>; 

ここで第二特性は、渡されたArrayタイプのconst性を維持します最初の人はそれを取り除く。確かに両方のユースケースがあります。std::beginでサポートされているコンテナ/配列のいずれかのタイプをサポートしてい非常に一般的なソリューションについては

+0

私はこのアプローチに非常に似ていますが、私はこれを 'std :: remove_reference'でラップしなければなりませんでした(例えば元の質問で' static_assert'テストを渡す) – Rook

+0

@ Rakete1111Thanks – SirGuy

+0

私の以前のコメントは間違っていました: 'const'要素を持つコンテナの場合、' std :: decay'はconstを取り除きます。代わりに 'std :: remove_reference_t'を使用してください。これはcv-qualifierを削除しないためです。 – Rakete1111