Using CRTP with partial class specialization? - c++

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...

Related

How to check if an object is an instance of a template class of multiple template arguments in C++?

I have the following class:
template <typename T, typename U = UDefault>
class A;
How to check whether types such as A<float> or A<float, int> are instances of the above templated class?
I tried modifying How to check if an object is an instance of a template class in C++? into:
template <typename T, typename U>
struct IsA : std::false_type
{};
template <typename T, typename U>
struct IsA<A<T, U>> : std::true_type
{};
but it's giving the following error
20:18: error: wrong number of template arguments (1, should be 2)
16:8: error: provided for 'template<class T, class U> struct IsA'
In function 'int main()':
40:34: error: wrong number of template arguments (1, should be 2)
16:8: error: provided for 'template<class T, class U> struct IsA'
How can this be solved?
Your IsA class should be expected to take one template argument. The type you are testing.
template <typename Type>
struct IsA : std::false_type {};
template <typename T, typename U>
struct IsA< A<T,U> > : std::true_type {};
// ^^^^^^ The specialization of your one template argument.
Or put alternately, since the individual template parameters to A do not matter here:
template <typename ...AParams>
struct IsA< A<AParams...> > : std::true_type {};
// ^^^^^^^^^^^^^ The specialization of your one template argument.
See it work in Compiler Explorer

SFINAE type trait with pointer-to-member-function fails

I'm practicing SFINAE and would like to implement a type trait in order to check if a given class T contains a method print(). I have the following two variants:
// (1)
template <typename T, typename = int>
struct has_print_method : std::false_type {};
template <typename T>
struct has_print_method<T, decltype(&T::print, 0)> : std::true_type {};
template <typename T>
const bool has_print_method_v = has_print_method<T>::value;
// (2)
template <typename T>
struct has_print_method{
template <typename U, typename = void> struct helper : std::false_type{};
template <typename U> struct helper<U, decltype(&U::print)> : std::true_type{};
static const bool value = helper<T, void (T::*)() const>::value;
};
template <typename T>
const bool has_print_method_v = has_print_method<T>::value;
(1) only checks for the existence of a member method print() and ignores the member method's signature. Whereas (2) checks the method's signature, i.e. it requires a member method void print() const.
I test both variants via:
#include <iostream>
#include <type_traits>
// Simple Class A
struct A{
int a;
public:
void print() const{}
};
// (1) or (2) here
template<typename T, std::enable_if_t<has_print_method_v<T>, bool> = true>
void print(T t) {
t.print();
}
void print(double x){
std::cout << x << '\n';
}
int main() {
A a;
print(a);
print(1.0); // (*)
return 0;
}
Using the type trait (1) and compiling with clang 12.0 and std=c++17 flag works as expected. However, using (2) instead, I obtain
<source>:28:50: error: member pointer refers into non-class type 'double'
static const bool value = helper<T, void (T::*)() const>::value;
^
<source>:32:33: note: in instantiation of template class 'has_print_method<double>' requested here
const bool has_print_method_v = has_print_method<T>::value;
^
<source>:34:39: note: in instantiation of variable template specialization 'has_print_method_v<double>' requested here
template<typename T, std::enable_if_t<has_print_method_v<T>, bool> = true>
^
<source>:35:6: note: while substituting prior template arguments into non-type template parameter [with T = double]
void print(T t) {
^~~~~~~~~~~~
<source>:47:5: note: while substituting deduced template arguments into function template 'print' [with T = double, $1 = (no value)]
print(1.0);
^
1 error generated.
What am I missing here? You can find the example here on godbolt. Edit: Um, I have just noticed that both versions compile without an error with gcc11.1. Strange.
First up, judging by your error messages and my own tests, I think you mean that type trait (1) works and (2) doesn't. On that basis, here is a version of trait (1) that tests for a matching function signature:
template <typename T, typename = int>
struct has_print_method : std::false_type {};
template <typename T>
struct has_print_method<T, decltype(&T::print, 0)> : std::is_same <decltype(&T::print), void (T::*)() const> {};
template <typename T>
const bool has_print_method_v = has_print_method<T>::value;
Live demo

Deduce template parameter of class

Can someone please help me to understand why the following code does not compile:
#include <type_traits>
template< typename T >
class A
{};
template< typename T >
class B
{};
template< template <typename T> class GENERAL_t, // Note: GENERAL_t is either A<T> or B<T>
typename = std::enable_if_t< std::is_same<T,int>::value >
>
void foo( GENERAL_t a )
{}
error message:
t.cpp:67:57: error: use of undeclared identifier 'T'
typename = std::enable_if_t< std::is_same<T,int>::value >
^
t.cpp:67:65: error: no type named 'value' in the global namespace
typename = std::enable_if_t< std::is_same<T,int>::value >
~~^
t.cpp:69:15: error: use of class template 'GENERAL_t' requires template arguments
void foo( GENERAL_t a )
^
t.cpp:66:43: note: template is declared here
template< template <typename T> class GENERAL_t, // Note: GENERAL_t is either A<T> or B<T>
~~~~~~~~~~~~~~~~~~~~~ ^
3 errors generated.
Here, foo should take instances of class A or class B but only when the template argument T of A or B is an int.
you haven't declared T. It needs to be a template parameter.
drop the T in the template <class T> class GENERAL_t
GENERAL_t is a template template and as such requires a template parameter.
please don't use ALL_CAPS for anything except macros
This is the working code:
template<class T, template <class> class General_t,
class = std::enable_if_t< std::is_same<T,int>::value >
>
void foo(General_t<T> a)
{}
bolov's answer is correct in all respects.
But in this case, you don't need the SFINAE on is_same. You can just template on the template-template:
template <template <class> class General>
void foo(General<int> ) { }

how to get template parameter of a template template parameter?

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;
};
// ...

Curiously Recurring Templates?

I have a problem that Curiously Recurring Templates could help quite nicely, but I cant even get past a simple test.
template<typename T, int _size, typename OutterT>
class Foo {
};
template<typename T>
class Bar : public Foo<T, 2, Bar> {};
//typedef Bar<float> Vec2f;
int main()
{
return 0;
}
This results in the error
foo.cpp:7: error: type/value mismatch at argument 3 in template parameter list for ‘template<class T, int _size, class OuterT> class Foo’
foo.cpp:7: error: expected a type, got ‘Bar’
What am I missing.
compiled with g++ 4.2.1
template<typename T, int _size, typename OutterT>
class Foo {
};
template<typename T>
class Bar : public Foo<T, 2, Bar<T> > {};
// ^^^
Bar<float> x;
Since Bar is a template, you must provide the template argument to instantiate it into a class.