2017-01-30 6 views
4

私は解決策を探していましたが、私が達成しようとしていることの正確な定義や言語の構文がわからないかもしれないので、投稿することにしました。汎用テンプレート関数を使用して、メンバーが異なるオブジェクトを処理する方法は?

私はそうのような特定のオブジェクト/構造体を持っている:

struct A 
{ 
    char myChar; 
    bool hasArray = false; 
}; 

template <uint8_t ARRAY_LEN> 
struct AA : public A 
{ 
    hasArray = true; 
    uint8_t myArray[ARRAY_LEN]; 
}; 

私はこれらのオブジェクト型の両方に取ることができ、派生のための共通の作業だけでなく、特定の作業を実行するための一般的な機能を作成したいstruct AA 。以下のような何か:

template <typename T> 
void func(T (&m)) 
{ 
    if (T.hasArray) 
    { 
    // do some processing with m.myArray 
    std::cout << sizeof(m.myArray) << std::endl; 
    // ... 
    } 
    // common processing 
    std::cout << "myChar: " << m.myChar << std::endl; 
}; 

私はそうのような関数を呼び出すことができるようにしたい:

これは私の意図を説明する一例であり、それは私が望むものをまとめて付与
A a; 
AA aa; 
func(a); // compiler error, this would not work as no array member 
func(aa); // this works 

する。実際のコードははるかに複雑で多くのオブジェクトが含まれています。私は過負荷になる可能性があることを知っていますが、私は1つの汎用機能でそれを行う方法があるかどうかを知りたいのですか?また、回避策や欠けている他のいくつかのC++機能があるかどうかを知りたいサンプルコードについて、コンパイラが不平を言う理由を理解しています。私はどんなタイプのキャスティングもしたくない... - C++ 11とGCC 4.8.5の使用

+1

C++ 17までは、1つの関数内でこれを行う方法がありません。 Constexpr if文が 'has_myArray'特性を持っていると、この動作が可能になります。 – 0x499602D2

+1

@ 0x499602D2これはC++です – Yakk

答えて

1

あなたのインスタンスを変更したくない場合はオーバーロードは、あなたのケースではうまく動作します:

#include<iostream> 
#include<cstdint> 

struct A 
{ 
    char myChar; 
}; 

template <uint8_t ARRAY_LEN> 
struct AA : public A 
{ 
    uint8_t myArray[ARRAY_LEN]; 
}; 

void func(const A &m) 
{ 
    std::cout << "myChar: " << m.myChar << std::endl; 
}; 

template <uint8_t AL> 
void func(const AA<AL> &m) 
{ 
    std::cout << sizeof(m.myArray) << std::endl; 
    func(static_cast<const A &>(m)); 
} 

int main() { 
    func(A{}); 
    func(AA<1>{}); 
} 

それでもテンプレート関数とsfinaeのビットと一緒に行きたい場合は、私はおそらく何かを使用します代わりに、このような:

#include<iostream> 
#include<cstdint> 

struct A 
{ 
    char myChar; 
}; 

template <uint8_t ARRAY_LEN> 
struct AA : public A 
{ 
    uint8_t myArray[ARRAY_LEN]; 
}; 

void func(A &m) 
{ 
    std::cout << "myChar: " << m.myChar << std::endl; 
} 

template <typename T> 
auto func(T &m) -> decltype(m.myArray, void()) 
{ 
    std::cout << sizeof(m.myArray) << std::endl; 
    A &a = m; 
    func(a); 
} 

int main() { 
    AA<1> aa{}; 
    A a{}; 
    func(a); 
    func(aa); 
} 

注両方のケースでは、あなたが実際にhasArrayメンバーデータを必要としないこと。

2

これはかなり複雑なC++ 14の機能です。 C++ 17はこれを簡単にするためにif constexprを導入しました。しかしそれは実行可能です。

template<std::size_t I> 
using index_t=std::integral_constant<std::size_t, I>; 
template<std::size_t I> 
constexpr index_t<I> index{}; 

constexpr inline index_t<0> dispatch_index() { return {}; } 
template<class B0, class...Bs, 
    std::enable_if_t<B0::value, int> =0 
> 
constexpr index_t<0> dispatch_index(B0, Bs...) { return {}; } 
template<class B0, class...Bs, 
    std::enable_if_t<!B0::value, int> =0 
> 
constexpr auto dispatch_index(B0, Bs...) { 
    return index< 1 + dispatch_index(decltype(Bs){}...) >; 
} 

template<class...Bs> 
auto dispatch(Bs...) { 
    using I = decltype(dispatch_index(decltype(Bs){}...)); 
    return [](auto&&...args)->decltype(auto){ 
    return std::get<I::value>(std::make_tuple(decltype(args)(args)..., [](auto&&...){})); 
    }; 
} 

dispatch(some_test)auto&&...をとるラムダを返します。 some_testが真似型の場合は最初の引数を返し、some_testがfalse-like型の場合は2番目の引数(または2番目の引数がない場合は[](auto&&...){})を返します。

次に、myArrayを検出するコードを記述します。

namespace details { 
    template<template<class...>class Z, class=void, class...Ts> 
    struct can_apply:std::false_type{}; 
    template<template<class...>class Z, class...Ts> 
    struct can_apply<Z, std::void_t<Z<Ts...>>, Ts...>:std::true_type{}; 
} 
template<template<class...>class Z, class...Ts> 
using can_apply = typename details::can_apply<Z, void, Ts...>::type; 

template<class T> 
using myArray_type = decltype(std::declval<T>().myArray); 

template<class T> 
using has_myArray = can_apply< myArray_type, T >; 

has_myArray<T>Tがメンバー.myArrayを持っている場合はtrueのようです。

我々は、これらが一緒に

dispatch(has_myArray<T>{})(
    [&](auto&& m) { 
    // do some processing with m.myArray 
    std::cout << sizeof(m.myArray) << std::endl; 
    // ... 
    } 
)(m); 

、今途中でラムダはm.myArrayが有効である場合にのみ実行されるフック。

単なる存在以上のものをチェックする、より複雑なテストを書くことができますが、通常これで十分です。MSVC 2015のような非C++ 11コンパイラで

class = std::enable_if_t<B0::value> 

class = std::enable_if_t<!B0::value>, class=void 

std::enable_if_t<B0::value, int> =0 

std::enable_if_t<!B0::value, int> =0 

を置き換えますそれぞれ

である。はい、これらは醜いです。 MSVCコンパイラチームに相談してください。

コンパイラにC++ 14が不足している場合は、void_tを書き、自分自身でenable_if_tと書くか、enable_ifを使って醜い長いバージョンを使用する必要があります。

さらに、テンプレート変数indexは、C++ 11では不正です。 index<blah>index_t<blah>{}に交換してください。

auto&&ラムダの欠如は上記を非常に痛ましいものにします。ラムダをアウトオブラインの関数オブジェクトに変換する必要があります。しかし、最初のC++ 14の機能の1つが実装されたオートラムダは、C++ 11を終了する前に実装されていました。

上記のコードは堅実に設計されていますが、誤植を含んでいる可能性があります。

1

一般的な機能を使用する方法がありますか?

あなたは、この機能でsizeof(m.myArray)を挿入した場合、あなたはmyArrayメンバーなしタイプでそれを呼び出すことはできませんので、私は、そうは思いません。たとえコンパイラがコンパイルする必要があるため、実行時にコードの一部であっても実行されません。

しかし、私が正しく理解していれば、あなたの構造体にmyArrayのメンバーがあるかどうかをあなたのhasArrayは言う。 myArraymyArray:だから

struct A 
{ 
    static constexpr bool hasArray { false }; 

    char myChar { 'z' }; 
}; 

template <uint8_t ARRAY_LEN> 
struct AA : public A 
{ 
    static constexpr bool hasArray { true }; 

    uint8_t myArray[ARRAY_LEN]; 
}; 

を次のように私は、あなたがstatic constexprメンバーにそれを変換することができたとし、func()では、次の2つのケースを選択するために、第二の機能、func2()を呼び出すことができます。あなたはこれにSFINAEを使うことができますが、(IMHO)はこの場合、より良いタグディスパッチです。だから、あなたはあなたのhasArray

template <typename T> 
void func2 (T const & m, std::true_type const &) 
{ std::cout << sizeof(m.myArray) << ", "; } 

template <typename T> 
void func2 (T const &, std::false_type const &) 
{ } 

template <typename T> 
void func(T (&m)) 
{ 
    func2(m, std::integral_constant<bool, T::hasArray>{}); 

    // common processing 
    std::cout << "myChar: " << m.myChar << std::endl; 
} 

異なるタイプに変換することができます今、あなたは type_traitsiostreamを含めることを忘れないでください、両方のタイプ

int main() 
{ 
    A  a; 
    AA<12U> aa; 

    func(a); // print myChar: z 
    func(aa); // print 12, myChar: z 
} 

func()を呼び出すことができます。

関連する問題