how to get template parameter of a template template parameter? - c++

Assume std::vector didnt have a value_type. Is is possible to write a template to deduce the value_type? Or more general, given a T<X>, how can I deduce X?
A very naive..
template <template <typename X> T>
void test(T<X> t) {
X x;
}
will probably make anybody who knows a bit about templates laugh at my foolish attempt and when instantiated like this:
int main() {
std::vector<int> x;
test(x);
}
create the following errors:
error: expected ‘class’ before ‘T’
template < template<typename X> T>
^
error: ‘X’ was not declared in this scope
void test(T<X> u) {
^
error: template argument 1 is invalid
void test(T<X> u) {
^
In function ‘void test(int)’:
error: ‘X’ was not declared in this scope
X x;
^
error: expected ‘;’ before ‘x’
X x;
^
In function ‘int main()’:
error: no matching function for call to ‘test(std::vector<int>&)’
test(x);
^
note: candidate is:
note: template<template<class X> class T> void test(int)
void test(T<X> u) {
^
note: template argument deduction/substitution failed:
note: cannot convert ‘x’ (type ‘std::vector<int>’) to type ‘int’
EDIT: the first one is easy to fix, but fixing it wont affect the others...
PS: I think I have a small misunderstanding, as std::vector<int> isnt a template, but a concrete type. However, still I would like to know if there is a way to get the int from a someTemplate<int> with some template magic.

You can create a traits to extract those parameter:
template <typename T> struct first_param;
template <template <typename, typename...> class C, typename T, typename ...Ts>
struct first_param<C<T, Ts...>>
{
using type = T;
};
For pre C++11, you have to handle number of parameters until acceptable values:
template <typename T> struct first_param;
template <template <typename> class C, typename T>
struct first_param<C<T>>
{
typedef T type;
};
template <template <typename, typename> class C, typename T, typename T2>
struct first_param<C<T, T2>>
{
typedef T type;
};
template <template <typename, typename, typename> class C,
typename T, typename T2, typename T3>
struct first_param<C<T, T2, T3>>
{
typedef T type;
};
// ...

Related

Template default argument

How do I specify a template class as a default value for a template typename? e.g. the following doesn't work.
template <typename A, typename B> class X {};
template <typename T=template <typename, typename> class X> class Y {};
int main()
{
Y<> y;
return 0;
}
tmp.cc:4:22: error: expected type-specifier before ‘template’
template <typename T=template <typename, typename> class X> class Y {};
^~~~~~~~
tmp.cc:4:22: error: expected ‘>’ before ‘template’
tmp.cc: In function ‘int main()’:
tmp.cc:8:7: error: template argument 1 is invalid
Y<> y;
^
The problem of your code is that for Y you ask a type template parameter and you want to use a template template parameter.
template <typename A, typename B> class X {};
// type template template
//........VVVVVVVVVV VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
template <typename T=template <typename, typename> class X> class Y {};
int main()
{
Y<> y;
return 0;
}
If you want to use X as default, T must be be a template template, so
template <typename, typename>
class X
{};
template <template <typename, typename> class T = X>
class Y
{};
int main ()
{
Y<> y;
}
Or also using variadics to improve flexibility
template <template <typename...> class T = X>
class Y
{};

SFINAE on Error in Dependent Type causes unexpected hard error

I have code that I can simplify down to something like this:
#include <type_traits>
template <typename T>
struct dependent
{
using type = typename T::type;
};
template <typename T>
typename dependent<T>::type
foo(const T& x);
bool foo(bool x) { return x; }
int main()
{
foo(true);
}
This fails to compile with g++ 9.3 with --std=c++17 with the error:
test.cpp: In instantiation of 'struct dependent<bool>':
test.cpp:11:1: required by substitution of 'template<class T> typename dependent<T>::type foo(const T&) [with T = bool]'
test.cpp:17:13: required from here
test.cpp:6:11: error: 'bool' is not a class, struct, or union type
6 | using type = typename T::type;
| ^~~~
This is not what I would expect. I would expect that attempting to substitute bool for T in template <typename T> typename dependent<T>::type foo(const T& x) would be a failure, which is not an error. It seems SFINAE is not working for me, but I do not know why.
From the examples in the unofficial reference on SFINAE:
Substitution proceeds in lexical order and stops when a failure is encountered.
template <typename A>
struct B { using type = typename A::type; };
template <
class T,
class = typename T::type, // SFINAE failure if T has no member type
class U = typename B<T>::type // hard error if T has no member type
// (guaranteed to not occur as of C++14)
> void foo (int);
I am hitting the case on class U = typename B<T>::type, but the "guaranteed to not occur as of C++14" bit seems to indicate that this should not be happening as of C++14. What gives?
Issue is that dependent<T> has type, but that one might be ill formed causing hard failure.
You might make dependent SFINAE friendly:
template <typename T, typename Enabler = void>
struct dependent
{
};
template <typename T>
struct dependent<T, std::void_t<typename T::type>>
{
using type = typename T::type;
};
Demo

Using CRTP with partial class specialization?

I'm trying to mix-in an operator [] with a class. My problem is I've partially specialized the class, and the compiler doesn't like me not specifying the template parameters to the derived class:
#include <iostream>
#include <type_traits>
using namespace std;
template <typename T>
struct mixin {
template <typename U>
void operator[](U u) {
cout << u;
}
};
template <typename T, typename = void>
struct derived : mixin<derived> {};
template <typename T>
struct derived<T,
typename enable_if<
is_same<T, int>{}
>::type> : mixin<derived> {};
int main() {
derived<int> d;
d[3.14];
}
With clang this gives:
test.cc:16:24: error: use of class template 'derived' requires template arguments
struct derived : mixin<derived> {};
^~~~~~~
test.cc:16:8: note: template is declared here
struct derived : mixin<derived> {};
^
test.cc:23:22: error: use of class template 'derived' requires template arguments
>::type> : mixin<derived> {};
^~~~~~~
test.cc:16:8: note: template is declared here
struct derived : mixin<derived> {};
^
gcc is even less helpful:
test.cc:16:31: error: type/value mismatch at argument 1 in template parameter list for ‘template<class T> struct mixin’
struct derived : mixin<derived> {};
^
test.cc:16:31: note: expected a type, got ‘derived’
test.cc:23:29: error: type/value mismatch at argument 1 in template parameter list for ‘template<class T> struct mixin’
>::type> : mixin<derived> {};
^
test.cc:23:29: note: expected a type, got ‘derived’
test.cc: In function ‘int main()’:
Is my only option to re-specify the template parameters inside of the mixin clause?
Well, try this:
#include <iostream>
#include <type_traits>
using namespace std;
template <typename T>
struct mixin {
template <typename U>
void operator[](U u) {
cout << u;
}
};
template <typename T, typename = void>
struct derived : mixin<derived<T>> {};
template <typename T>
struct derived<T,
typename enable_if<
is_same<T, int>::value
>::type> : mixin<derived<T>> {};
int main() {
derived<int> d;
d[3.14];
}
It does work...
What I changed:
Using is_same<foo,bar>::value, not is_same<foo,bar>{} edit: Hmm, it seems you don't need to change that after all. Neat!
Not trying to get the compiler to deduce the template parameter for derived when using mixin<derived>. You were being way too optimistic there...

Use enable_if with is_integral to make distribution traits

I want to make a traits for std::uniform_*_distribution according to type given. E.g.:
distribution_traits<float>::type int_dist;
I tried following ways, but none of them compiles, and I don't know why.
Implementation 1
Use std::enable_if with typedefs:
template <typename T>
struct distribution_traits {
using type = typename std::enable_if<std::is_integral<T>::value, std::uniform_int_distribution<T>>::type;
using type = typename std::enable_if<std::is_floating_point<T>::value, std::uniform_real_distribution<T>>::type;
};
Clang 3.4 complains:
dist_traits.cpp:7:9: error: redefinition of 'type'
using type = typename std::enable_if<std::is_floating_point<T>::value, std::uniform_real_distribution<T>>::type;
^
dist_traits.cpp:6:9: note: previous definition is here
using type = typename std::enable_if<std::is_integral<T>::value, std::uniform_int_distribution<T>>::type;
^
dist_traits.cpp:6:40: error: no type named 'type' in 'std::enable_if<false, std::uniform_int_distribution<float> >'; 'enable_if' cannot be used to
disable this declaration
using type = typename std::enable_if<std::is_integral<T>::value, std::uniform_int_distribution<T>>::type;
^~~~~~~~~~~~~~~~~~~~~~~~~~
dist_traits.cpp:28:3: note: in instantiation of template class 'distribution_traits<float>' requested here
distribution_traits<float>::type int_dist;
^
2 errors generated.
Implementation 2
Use enable_if as class template parameter:
template <typename T, typename distribution_t = void>
struct distribution_traits;
template <typename T>
struct distribution_traits<
T, typename std::enable_if<std::is_integral<T>::value,
std::uniform_int_distribution<T> >::type > {
using type = std::uniform_int_distribution<T>;
};
template <typename T>
struct distribution_traits<
T, typename std::enable_if<std::is_floating_point<T>::value,
std::uniform_real_distribution<T> >::type > {
using type = std::uniform_real_distribution<T>;
};
And Clang complains
dist_traits.cpp:28:3: error: implicit instantiation of undefined template 'distribution_traits<float, void>'
distribution_traits<float>::type int_dist;
^
Either way cannot be compiled by MSVC++ 12.0, and the error messages are similar.
Could anyone please explain what's wrong I'm doing with SFINAE? Thanks!
For those who are curious about solution, here is the one that compiles:
template <typename T>
auto dist() -> typename std::enable_if<std::is_integral<T>::value, std::uniform_int_distribution<T>>::type;
template <typename T>
auto dist() -> typename std::enable_if<std::is_floating_point<T>::value, std::uniform_real_distribution<T>>::type;
template <typename T>
struct distribution_traits {
using type = decltype(dist<T>());
};
BTW, if put dist function into distribution_traits, the compilation will fail with error: function only differs in return type cannot be overloaded. :(
SFINAE can be used to discard overloads of function templates and class template specializations during substitution of template arguments.
It cannot be used with type/template aliases like you're trying to do.
About your working code - putting dist inside the class doesn't work because you attempt to call dist inside decltype without an object. Make dist static and it'll work:
template <typename T>
struct distribution_traits {
template <typename U>
static auto dist() -> typename std::enable_if<std::is_integral<U>::value, std::uniform_int_distribution<U>>::type;
template <typename U>
static auto dist() -> typename std::enable_if<std::is_floating_point<U>::value, std::uniform_real_distribution<U>>::type;
using type = decltype(dist<T>());
};
For implementation 2 to work, you need to omit the second argument of enable_if:
template
struct distribution_traits;
template <typename T>
struct distribution_traits<
T, typename std::enable_if<std::is_integral<T>::value>::type> {
using type = std::uniform_int_distribution<T>;
};
otherwise the specialization you define is distribution_traits<T, uniform_int_distribution<T>> and that doesn't match an instantiation like distribution_traits<float> because the second parameter is defaulted to void.

SFINAE working in return type but not as template parameter

I already used the SFINAE idiom quite a few times and I got used to put my std::enable_if<> in template parameters rather than in return types. However, I came across some trivial case where it didn't work, and I'm not sure why. First of all, here is my main:
int main()
{
foo(5);
foo(3.4);
}
Here is an implementation of foo that triggers the error:
template<typename T,
typename = typename std::enable_if<std::is_integral<T>::value>::type>
auto foo(T)
-> void
{
std::cout << "I'm an integer!\n";
}
template<typename T,
typename = typename std::enable_if<std::is_floating_point<T>::value>::type>
auto foo(T)
-> void
{
std::cout << "I'm a floating point number!\n";
}
And here is a supposedly equivalent piece of code that works fine:
template<typename T>
auto foo(T)
-> typename std::enable_if<std::is_integral<T>::value>::type
{
std::cout << "I'm an integrer!\n";
}
template<typename T>
auto foo(T)
-> typename std::enable_if<std::is_floating_point<T>::value>::type
{
std::cout << "I'm a floating point number!\n";
}
My question is: why does the first implementation of foo triggers that error while the second one does not trigger it?
main.cpp:14:6: error: redefinition of 'template<class T, class> void foo(T)'
auto foo(T)
^
main.cpp:6:6: note: 'template<class T, class> void foo(T)' previously declared here
auto foo(T)
^
main.cpp: In function 'int main()':
main.cpp:23:12: error: no matching function for call to 'foo(double)'
foo(3.4);
^
main.cpp:6:6: note: candidate: template<class T, class> void foo(T)
auto foo(T)
^
main.cpp:6:6: note: template argument deduction/substitution failed:
main.cpp:5:10: error: no type named 'type' in 'struct std::enable_if<false, void>'
typename = typename std::enable_if<std::is_integral<T>::value>::type>
^
EDIT :
Working code and faulty code.
You should take a look at 14.5.6.1 Function template overloading (C++11 standard) where function templates equivalency is defined. In short, default template arguments are not considered, so in the 1st case you have the same function template defined twice. In the 2nd case you have expression referring template parameters used in the return type (again see 14.5.6.1/4). Since this expression is part of signature you get two different function template declarations and thus SFINAE get a chance to work.
Values in the templates work:
template<typename T,
typename std::enable_if<std::is_integral<T>::value, int>::type = 0>
auto foo(T)
-> void
{
std::cout << "I'm an integer!\n";
}
template<typename T,
typename std::enable_if<std::is_floating_point<T>::value, int>::type = 0>
auto foo(T)
-> void
{
std::cout << "I'm a floating point number!\n";
}
The = ... of the template just gives a default parameter. This is not part of the actual signature which looks like
template<typename T, typename>
auto foo(T a);
for both functions.
Depending on your needs, the most generic solution for this problem is using tag dispatching.
struct integral_tag { typedef integral_tag category; };
struct floating_tag { typedef floating_tag category; };
template <typename T> struct foo_tag
: std::conditional<std::is_integral<T>::value, integral_tag,
typename std::conditional<std::is_floating_point<T>::value, floating_tag,
std::false_type>::type>::type {};
template<typename T>
T foo_impl(T a, integral_tag) { return a; }
template<typename T>
T foo_impl(T a, floating_tag) { return a; }
template <typename T>
T foo(T a)
{
static_assert(!std::is_base_of<std::false_type, foo_tag<T> >::value,
"T must be either floating point or integral");
return foo_impl(a, typename foo_tag<T>::category{});
}
struct bigint {};
template<> struct foo_tag<bigint> : integral_tag {};
int main()
{
//foo("x"); // produces a nice error message
foo(1);
foo(1.5);
foo(bigint{});
}