4

次のようなテンプレート化された関数を呼び出そうとしています:

typedef std::tuple<int, double, bool> InstrumentTuple;

Cache cache;
InstrumentTuple tuple = cache.get<InstrumentTuple>();

タプルの型を「単純に」渡すことができることはわかっています。これは私が知っていることですが、この関数を何度も呼び出し、タプルが非常に長いため、非常に面倒です。

InstrumentTuple tuple = c.get<int, double, bool>(); // syntax I'd like to avoid

だから私は get メソッドの複数の実装を試みましたが、成功しませんでした:

テンプレート パラメーターによる有効化

#include <tuple>

class Cache
{
private:
    template<int I, typename T, typename = typename std::enable_if<I == std::tuple_size<T>::value>::type>
    std::tuple<> get() // line 6
    {
        return std::tuple<>();
    }

    template<int I, typename T, typename = typename std::enable_if<I != std::tuple_size<T>::value>::type>
    std::tuple<typename std::tuple_element<I,T>::type, decltype(get<I+1, T>())> get() // line 12
    {
        std::tuple<typename std::tuple_element<I,T>::type> value;
        return std::tuple_cat(value, get<I+1, T>());
    }

public:
    template<typename T>
    T get()
    {
        return get<0, T>(); // line 22
    }
};

int main(int argc, char** argv)
{
    Cache cache;
    typedef std::tuple<int, double, bool> InstrumentTuple;
    InstrumentTuple tuple = cache.get<InstrumentTuple>(); // line 30
}

これは私にこのエラーを与えます:

main.cpp: In instantiation of 'T Cache::get() [with T = std::tuple<int, double, bool>]':
main.cpp:30:56:   required from here
main.cpp:22:26: error: no matching function for call to 'Cache::get()'
main.cpp:22:26: note: candidates are:
main.cpp:6:18: note: template<int I, class T, class> std::tuple<> Cache::get()
main.cpp:6:18: note:   template argument deduction/substitution failed:
main.cpp:5:33: error: no type named 'type' in 'struct std::enable_if<false, void>'
main.cpp:12:81: note: template<int I, class T, class> std::tuple<typename std::tuple_element<I, T>::type, decltype (get<(I + 1), T>())> Cache::get()
// ----- Important part
main.cpp:12:81: note:   template argument deduction/substitution failed:
main.cpp: In substitution of 'template<int I, class T, class> std::tuple<typename std::tuple_element<I, T>::type, decltype (get<(I + 1), T>())> Cache::get() [with int I = 0; T = std::tuple<int, double, bool>; <template-parameter-1-3> = <missing>]':
// -----
main.cpp:22:26:   required from 'T Cache::get() [with T = std::tuple<int, double, bool>]'
main.cpp:30:56:   required from here
main.cpp:12:81: error: no matching function for call to 'Cache::get()'
main.cpp:12:81: note: candidate is:
main.cpp:6:18: note: template<int I, class T, class> std::tuple<> Cache::get()
main.cpp:6:18: note:   template argument deduction/substitution failed:
main.cpp:5:33: error: no type named 'type' in 'struct std::enable_if<false, void>'
main.cpp: In instantiation of 'T Cache::get() [with T = std::tuple<int, double, bool>]':
main.cpp:30:56:   required from here
main.cpp:20:7: note: template<class T> T Cache::get()
main.cpp:20:7: note:   template argument deduction/substitution failed:
main.cpp:22:26: error: wrong number of template arguments (2, should be 1)

テンプレート パラメータが欠落している理由がわかりません。

だから私は別の実装を試しました:

テンプレート - テンプレートという名前のパラメーター

#include <tuple>

class Cache
{
private:
    template<int>
    std::tuple<> get() // line 7
    {
        return std::tuple<>();
    }

    template<int index, typename type, typename... rest>
    std::tuple<type, rest...> get() // line 13
    {
        return std::tuple_cat(std::tuple<type>(), get<index+1, rest...>());
    }

public:
    template<template<typename... types> class tuple>
    typename std::tuple<(tuple::types)...> get()
    {
        return get<0, (tuple::types)...>();
    }
};  // line 24

int main(int argc, char** argv)
{
    Cache cache;
    typedef std::tuple<int, double, bool> InstrumentTuple;
    InstrumentTuple tuple = cache.get<InstrumentTuple>(); // line 30
}

しかし、私はこのエラーが発生します:

// ----- Important part
main.cpp:24:1: error: expected identifier before '}' token
main.cpp:24:1: error: expected unqualified-id before '}' token
// -----
main.cpp: In function 'int main(int, char**)':
main.cpp:30:56: error: no matching function for call to 'Cache::get()'
main.cpp:30:56: note: candidates are:
main.cpp:7:18: note: template<int <anonymous> > std::tuple<> Cache::get()
main.cpp:7:18: note:   template argument deduction/substitution failed:
main.cpp:13:31: note: template<int index, class type, class ... rest> std::tuple<_Head, _Tail ...> Cache::get()
main.cpp:13:31: note:   template argument deduction/substitution failed:

繰り返しますが、識別子の欠落によるエラーがわかりません。

私が達成したいことが可能であるかどうか、私は今疑問に思っています。好きなように使えstd::tupleますか?それとももっと良い方法がありますか?

4

2 に答える 2

6

get2 番目のオーバーロードが独自の戻り値の型宣言の時点で表示されないため、最初のソリューションは失敗しています。これを回避するには、戻り型の計算を独自のサブプログラムに分離する必要があります。

2 番目の解決策はより近いです。問題は、その引数ではなく、テンプレート のみを推測していることです。std::tuple可変個引数 (例えば、 への型引数tuple) を推測する簡単な方法は、空のタグ構造を使用することであり、1 つの余分なレベルの間接化が必要です。

template<typename T> struct type_tag {};

class Cache {
    // ... (as above)

    template<typename... Ts> std::tuple<Ts...> get(type_tag<std::tuple<Ts...>>) {
        return get<0, Ts...>();
    }

public:
    template<typename T> T get() {
        return get(type_tag<T>{});
    }
};

再帰の代わりにパック展開を使用してソリューションを記述できるかどうかを確認する必要があります。次に例を示します。

template<typename T> struct type_tag {};

class Cache {
    template<typename... Ts> std::tuple<Ts...> get(type_tag<std::tuple<Ts...>>) {
        return std::tuple<Ts...>{Ts{}...};
    }

public:
    template<typename T> T get() {
        return get(type_tag<T>{});
    }
};
于 2013-03-11T12:43:18.647 に答える
3

これが私が思いついたものです:

#include <tuple>

struct Cache;

/* typename = std::tuple<...> */
template<int, typename> struct cache_getter;
/* typename = parameters from std::tuple<...> */
template<int, typename...> struct tuple_walker;

template<int I, typename... Ts> struct cache_getter<I, std::tuple<Ts...> > {
    static std::tuple<Ts...> get(Cache & c);
};

struct Cache {
protected:
    template<int, typename...> friend struct tuple_walker;
private:
    /* here T is a type from within a std::tuple<...> */
    template<int I, typename T> std::tuple<T> get_ex() {
        return std::tuple<T>();
    }
public:
    /* here T is actually a std::tuple<...> */
    template<typename T> T get() {
        return cache_getter<0, T>::get(*this);
    }
};

/* since std::tuple_cat only accepts 2 std::tuples per call but we don't have control over the number of types in the passed in std::tuple, we'll need to chain our calls */
template<typename...> struct my_tuple_cat;
template<typename H, typename... T> struct my_tuple_cat<H, T...> {
    static auto cat(H h, T... t) -> decltype(std::tuple_cat(h, my_tuple_cat<T...>::cat(t...)))
    { return std::tuple_cat(h, my_tuple_cat<T...>::cat(t...)); }
};
template<typename T> struct my_tuple_cat<T> {
    static T cat(T t) { return t; }
};

/* this one is used to call Cache.get_ex<int I, typename T>() with incrementing values for I */
template<int I, typename H, typename... T> struct tuple_walker<I, H, T...> {
    static std::tuple<H, T...> get(Cache & c) {
        return my_tuple_cat<std::tuple<H>, std::tuple<T...>>::cat(c.get_ex<I, H>(), tuple_walker<I + 1, T...>::get(c));
    }
};
template<int I, typename H> struct tuple_walker<I, H> {
    static std::tuple<H> get(Cache & c) {
        return c.get_ex<I, H>();
    }
};
/* this one will forward the types in std::tuple<...> to tuple_walker to get each tuple separately */
template<int I, typename... Ts> std::tuple<Ts...> cache_getter<I, std::tuple<Ts...> >::get(Cache & c) {
    return tuple_walker<I, Ts...>::get(c);
}

int main(int argc, char ** argv) {
    Cache cache;
    typedef std::tuple<int, double, bool> InstrumentTuple;
    InstrumentTuple tuple = cache.get<InstrumentTuple>();
    return 0;
}

これが何かの価値があることを願っています。私はまだ C++11 で多くのことを行っていないので、これは最適な解決策ではないかもしれません。

コンパイルされていることの証明はここにあります

于 2013-03-11T10:59:58.827 に答える