The Eyesight Dim The Eyesight Dim - 1 month ago 10x
C++ Question

How to form a variadic template function within a non-variadic template class?

So, as I've been learning about templates in C++, I decided to come up with some unusual situations to see if I could get them to work. (No, it's not practical - just to play with the language!) I made a template class that holds a value of type

, with a variadic function template that returns a
and the maximum of one of the values in the parameter pack. However, I can't get it to compile. Here's what I wrote...

In header.h:

#ifndef HEADER_H
#define HEADER_H

#include <utility>
#include <algorithm>
#include <array>

template <typename T>
class MyClass

T m_heldType;


MyClass(T t) : m_heldType(t) {}

template <typename... T2>
std::pair<T, T2> pairingFunc(T2&&... types)
std::array<T2, sizeof...(types)> types_arr = { types... };

return std::make_pair( m_heldType, *std::max_element(types_arr.begin(), types_arr.end()) );



In source.cpp:

#include <iostream>
#include "Header.h"

int main()
MyClass<int> mc(512);

auto mypair = mc.pairingFunc(1.61f, 3.14f, 6.66f);

std::cout << mypair.first << ' ' << mypair.second;

These are the errors I generate:

Error C3520 'T2': parameter pack must be expanded in this context ...\header.h 24
Error C2672 'MyClass<int>::pairingFunc': no matching overloaded function found ...\source.cpp 8
Error C2893 Failed to specialize function template 'std::pair<T,T2> MyClass<T>::pairingFunc(T2 &&...)' ...\source.cpp 8
Error C3536 'mypair': cannot be used before it is initialized ...\source.cpp 10
Error C2228 left of '.first' must have class/struct/union ...\source.cpp 10
Error C2228 left of '.second' must have class/struct/union ...\source.cpp 10

So here's what I'm thinking:

  • Obviously, the compiler can't determine the type of
    (fails to initialize). But why? It knows the type of
    and the type of
    . Explicitly stating
    std::pair<int, float>
    fixes this error, but leaves the others (a symptom of the underlying issue).

  • "Failed to specialize function template"...which I guess means that it couldn't return the type based on the types given? If so, why not?

  • "parameter pack must be expanded in this context" - I'm not sure about this one. Doesn't that occur by putting the pack into an array?

Additionally, I'd like to enforce provision of at least one argument through something like
(T2&& head, T2&&... tail)
, but I think getting both of those into an array or vector could be nasty, and I'm not sure how to deal with just the single variadic as it is. So that's just a 'bonus' I guess.


The problem is here:

std::pair<T, T2> pairingFunc(T2&&... types)

and here

std::array<T2, sizeof...(types)> types_arr = { types... };

T2 is a parameter pack, not a type. It can store multiple types, i.e. 1.4f, "hi", 1, 0.5. So you cannot use it as a single type, it's just not possible. You need to introduce another parameter and use that as a type:

template <typename T1, typename... T2>
std::pair<T, T1> pairingFunc(T1&& arg, T2&&... types)
    std::array<T1, sizeof...(types) + 1> types_arr = { arg, types... };

    return std::make_pair(m_heldType, *std::max_element(types_arr.begin(), types_arr.end()));

This also has the advantage that if you call

mc.pairingFunc(4.5f, "hello");

it wouldn't compile. Calling it with no arguments is also not possible anymore (which wouldn't make sense nonetheless).

A preferable solution (thanks @DanielSchepler) might be to use std::common_type, as it may be possible that the second argument is not convertible to T, but T is convertible to the second argument:

template <typename T1, typename... T2>
std::pair<T, std::common_type_t<T1, T2...>> pairingFunc(T1&& arg, T2&&... types)
    std::array<std::common_type_t<T1, T2...>, sizeof...(types) + 1> types_arr = { arg, types... };

    return std::make_pair(m_heldType, *std::max_element(types_arr.begin(), types_arr.end()));