问题
Suppose I have the following class:
template <class T, class U, class V> Foo
{
...
};
The template parameters have a distinct mapping, so I can deduce the other template arguments U and V based on what T is. For example, if T is double, U and V will always be some classes D1 and D2, and if T is float, U and V will always be some other classes F1 and F2.
With that in mind, is there a way I can pass in only one template argument, and have the compiler deduce the other two parameters?
I know the simple answer would be to just make these other classes templated as well and pass the template argument T to them, but I am not able to make these classes templated (they are auto-generated by a tool).
Ideally I would be able to use typedef or #define like so:
typedef Foo<double> Foo<double, D1, D2>
typedef Foo<float> Foo<float, F1, F2>
However these do not compile. I am wondering if there's a way to use template metaprogramming or template template parameters to solve this issue, but I can't seem to wrap my head those concepts, and I have a gut feeling there's probably an even simpler answer out there. Anybody have any ideas?
回答1:
The answer given by Angew shows you the right approach, but does not show you how to cope with situations where U
and V
cannot be deduced and must be provided by the instantiating client.
To handle this case, you could assign default arguments for the template parameters U
and V
:
struct D1 { }; struct D2 { };
struct F1 { }; struct F2 { };
// Primary template
template<typename T>
struct deduce_from
{
};
// Specialization for double: U -> D1, V -> D2
template<>
struct deduce_from<double>
{
typedef D1 U;
typedef D2 V;
};
// Specialization for float: U -> F1, V -> F2
template<>
struct deduce_from<float>
{
typedef F1 U;
typedef F2 V;
};
// Give defaults to U and V: if deduce_from is not specialized for
// the supplied T, and U or V are not explicitly provided, a compilation
// error will occur
template<
typename T,
typename U = typename deduce_from<T>::U,
typename V = typename deduce_from<T>::V
>
struct Foo
{
typedef U typeU;
typedef V typeV;
};
And here is a simple program to test the correctness of the above solution:
#include <type_traits>
int main()
{
static_assert(std::is_same<Foo<double>::typeU, D1>::value, "Error!");
static_assert(std::is_same<Foo<double>::typeV, D2>::value, "Error!");
static_assert(std::is_same<Foo<float>::typeU, F1>::value, "Error!");
static_assert(std::is_same<Foo<float>::typeV, F2>::value, "Error!");
// Uncommenting this will give you an ERROR!
// No deduced types for U and V when T is int
/* static_assert(
std::is_same<Foo<int>::typeU, void>::value, "Error!"
); */
static_assert(
std::is_same<Foo<int, bool, char>::typeU, bool>::value, "Error!"
); // OK
static_assert(
std::is_same<Foo<int, bool, char>::typeV, char>::value, "Error!"
); // OK
}
回答2:
You could get rid of U
and V
, like this:
template <typename T>
struct Foo
{
typedef typename deduce_from<T>::U U;
typedef typename deduce_from<T>::V V;
};
where deduce_from
encapsulates the deduction process.
来源:https://stackoverflow.com/questions/15025704/c-deduce-template-arguments-based-on-other-template-arguments