I'd like to make a typedef
which is dependent on the existence of a typedef
in a template argument:
struct foo
{
using MyType = int;
};
template <typename T = foo>
struct bar
{
// Pseudo code
#if T::MyType is defined
using MyType = T::MyType;
#else
using MyType = double;
#endif
};
Is there a way to get it to work using std::conditional
or something else in C++14?
There is, with a bit of sfinae.
template<class, typename Fallback, typename = void>
struct type_or_default {
using type = Fallback;
};
template<class C, typename F>
struct type_or_default<C, F, std::void_t<typename C::type>> {
using type = typename C::type;
};
This uses the standard convention where template meta-functions expose a member name type
, but you can adapt it for your own naming needs. The only non-C++14 bit here is std::void_t
, but an equivalent thing can be implemented in C++14 (it just can't be put into namespace std
). You use it in your class like this:
template <typename T = foo>
struct bar
{
using type = typename type_or_default<T, double>::type;
};
What happens here is that the compiler does its pattern matching when choosing a template specialization. If the class C
has a member type
, then the partial specialization we provided will be considered more specialized, and as such chosen. Otherwise (if substitution fails when checking the specialization), the primary template is always there to fall back to.
Live program to tinker with.
My five cents to this question.
#include <type_traits>
template <typename T, typename DefaultType>
struct CalculateMyType {
template <typename C>
static typename C::MyType test(typename C::MyType*);
template <typename>
static DefaultType test(...);
typedef decltype(test<T>(nullptr)) MyType;
};
struct foo
{
using MyType = int;
};
template <typename T = foo>
struct bar
{
using MyType = typename CalculateMyType<T, double>::MyType;
};
struct baz
{
};
struct quux
{
using MyType = float;
};
#include <iostream>
#include <typeinfo>
template <typename>
struct TypeToStr;
template<> struct TypeToStr<double> { const char * name = "double"; };
template<> struct TypeToStr<float> { const char * name = "float"; };
template<> struct TypeToStr<int> { const char * name = "int"; };
int main() {
std::cout << "bar<foo>::MyType = " << TypeToStr<bar<foo>::MyType>().name << std::endl;
std::cout << "bar<baz>::MyType = " << TypeToStr<bar<baz>::MyType>().name << std::endl;
std::cout << "bar<quux>::MyType = " << TypeToStr<bar<quux>::MyType>().name << std::endl;
}
Live program
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With