4

I have the following code, where I have a template class, and a type in it, which I would like to use in a separate template function.

template <typename... Types>
struct MyClass
{
    enum SomeEnum { value0 = -1 };
};

template <typename... Types>
struct OtherClass
{
};

template <typename T, typename... Types>
T check(typename MyClass<Types...>::SomeEnum value) 
{
    OtherClass<Types...> obj;
    T result;
    // calculate result from obj;
    return result;
}

int main() {
    auto value = MyClass<int, bool>::value0;
    // ... 
    int t = check<int>(value);
}

I tought that the compiler will be able to deduce the parameter pack from the function call, so I can use it in the function template also. Unfortunately the compiler can't deduce it:

$ g++ -std=c++11 op.cpp

op.cpp: In function ‘int main()’:
op.cpp:25:27: error: cannot convert ‘MyClass<int, bool>::SomeEnum’ to ‘MyClass<>::SomeEnum’ for argument ‘1’ to ‘T check(typename MyClass<Types ...>::SomeEnum) [with T = int; Types = {}; typename MyClass<Types ...>::SomeEnum = MyClass<>::SomeEnum]’
   int t = check<int>(value);

Is there a solution to "transfer" the template parameter pack to the template function?

simon
  • 1,210
  • 12
  • 26

3 Answers3

1

Template arguments cannot be deduced from nested types. This isn't new or changed with variadic templates.

Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
1

Template deduction is not possible, but maybe you can restructure your code in a way that MyClass defines the all necessary types and then you have a check function that takes MyClass as a template argument. That way, the checking function has access to all the necessary types.

template <typename... Types> struct OtherClass {};

template <typename... Types>
struct MyClass
{
    typedef OtherClass<Types...> OtherClass_t;
    typedef int result_t;

    enum SomeEnum { value0 = -1 };
};

// version 1
template < typename C >
struct Checker {
    typename C::result_t operator()(typename C::SomeEnum value)
    {
        typename C::OtherClass_t obj;
        typename C::result_t result;
        // calculate result from obj;
        return result;
    }
};

// version 2
template < typename C >
typename C::result_t check_fun(typename C::SomeEnum value)
{
    typename C::OtherClass_t obj;
    typename C::result_t result;
    // calculate result from obj;
    return result;
}


int main() {
    typedef MyClass< int, bool > myclass_t;
    auto value = myclass_t::value0;
    // ... 
    Checker< myclass_t > check;
    int t = check(value);
    auto s = check_fun<myclass_t>(value);
}

The downside is of course, that you have to instantiate the checker class or call the function with the proper type of MyClass as template argument.

user1978011
  • 3,419
  • 25
  • 38
  • Thanks, it's a good idea, unfortunately in the actual code I have to keep the two class independent. – simon May 17 '15 at 23:29
  • While I have worked out a solution using tuples, I'm going to accept this answer because I have ended up restructuring my code instead. – simon May 26 '15 at 16:22
0

The template parameter pack can be passed over using std::tuple. A wrapper class needed over SomeEnum type to store the parameter pack by creating a tuple type from them:

template <typename... Types>
struct MyClass
{
    struct Value {
        enum SomeEnum { value0 = -1 };
        enum SomeEnum value;
        typedef std::tuple<Types...> TypeTuple;
    };
};

Than a helper class needed which feeds the template argument list of a template class from the tuple types:

template <
    template <typename...> class Class, 
    typename Tuple, typename T, T... nums>
struct Helper_ : Class <
    typename std::tuple_element<nums, Tuple>::type... > 
{};

template <template <typename...> class Class, typename Tuple>
struct Helper : Helper_<
    Class, Tuple, 
    make_integer_sequence<int, std::tuple_size<Tuple>::value > > 
{};

The check function then uses this helper class to instanciate the other class:

template <typename T, typename V>
T check(V value) 
{
    Helper<OtherClass, typename V::TypeTuple> obj;
    T result;
    // calculate result from obj;
    return result;
}

And the use of check function changes a bit becouse now it waits the wrapper type instead of the pure enum:

int main() {
    MyClass<int, bool, double>::Value value; 
    value.value = MyClass<int, bool, double>::Value::value0;

    int t = check<int>(value);
}
simon
  • 1,210
  • 12
  • 26