I essentially have a mock version of std::integral_constant that includes a variable and I want to specialize a function template for these classes derived from Base<T>, like this:
template<class T> struct Base{
typedef T type;
T t;
};
template<class T> struct A : Base<T>{
static constexpr T value = 1;
};
template<class T> struct B : Base<T>{
static constexpr T value = 2;
};
struct Unrelated{};
// etc.
template<class T> void foo(T t){
//I would like to specialize foo for A and B and have a version for other types
}
int main(){
foo(A<float>());//do something special based on value fields of A and B
foo(B<float>());
foo(Unrelated()); //do some default behavior
}
Here are the main issues:
I cannot include value as a template as I am expecting T = double, float, or some other non-integral types (otherwise I'd just extend std::integral_constant)
I can't cleanly use std::is_base as I would have to do std::is_base<Base<T::type>,T>
Doing foo(Base<T>&) wouldn't allow me to see value and I don't want to have to resort to a virtual value() function (or reflection).
And obviously I would like to avoid specializing foo for every derived class.
I think the answer lies in using is_base but I haven't been able to get it to work no matter how I tried to use it. Is there a much simpler way I am missing?
The following should work:
template<typename,typename = void>
struct IsBase
: std::false_type {};
template<typename T>
struct IsBase<T, typename std::enable_if<
std::is_base_of<Base<typename T::type>,T>::value
>::type>
: std::true_type {};
template<class T>
typename std::enable_if<IsBase<T>::value>::type foo(T t){
// use T::value
}
template<class T>
typename std::enable_if<!IsBase<T>::value>::type foo(T t){
// general case
}
Live example
Related
I have a class that can accept arithmetic types and std::complex. A simplified code of the class is
#include <complex>
template<typename T> struct is_complex : std::false_type {};
template<typename T> struct is_complex<std::complex<T>> : std::true_type {};
template<class T>
struct Foo {
void foo(typename T::value_type t)
requires (is_complex<T>::value) {
}
};
Now, I would like to take the internal type of std::complex and use it as the type of the parameters in the foo function.For example, if T is std::complex<double>, then I want the parameter types to be double.
This function should only be available when T is indeed std::complex.
I thought I could use typename T::value_type as the parameter type, since std::complex has a typedef value_type. Plus, I thought using requires here would avoid T to be substitued in this function in case T wasn't std::complex. Silly me.
The issue is that whenever I create a Foo<FundamentalType> the code breaks, since fundamentals don't have ::value_type.
int main() {
Foo<int> obj; // Breaks the code.
//obj.foo(4); // Function shouldn't be considered in overload resolution ideally...
Foo<std::complex<int>> obj2; // Works
obj2.foo(4); // Works as expected
}
Ideally, I would like the substitution of T to be ignored for this function in case T is not std::complex. Is that possible? If not, how can I circumvent this?
You're on the right track with is_complex: you'd like the same here, but with a different body of the type. For example,
template<typename T> struct complex_value_type {};
template<typename T> struct complex_value_type<std::complex<T>> { using type = T; };
template<typename T>
using complex_value_type_t = typename complex_value_type<T>::type;
Then, at any point, you can call it as complex_value_type_t<T>:
template<class T>
struct Foo {
template<typename T_ = T>
void foo(complex_value_type_t<T_> t)
requires (is_complex<T_>::value) {
}
};
The requires is not absolutely necessary then; it's already covered by complex_value_type_t<T> being defined only for complex<T>.
You just need some type to put in there, until requires can disable the function.
I would do this:
struct nullptr_value_type {using value_type = std::nullptr_t;};
using elem_or_null_t = typename std::conditional_t<is_complex<T>::value, T, nullptr_value_type>::value_type;
void foo(elem_or_null_t t)
requires (is_complex<T>::value)
{}
Use a template class as a template parameter.
#include <complex>
template<template<class> class T> struct is_complex : std::false_type {};
template<> struct is_complex<std::complex> : std::true_type {};
template<template<class> class T>
struct Foo {
void foo(typename T<double>::value_type t)//could be typename<T<TT>> if you made foo a templated function
requires (is_complex<T>::value) {
}
};
int main(){
Foo<std::complex> f;
};
But you will need what to put into it when used. I just hard-coded double but you have to add a new template parameter to Foo or make foo a templated member function
I have a template class A
template<template<class> class E, class N>
class A
{}
and I also have a template function that takes A as an argument:
template<class T>
auto make_something(const T& t){}
used like make_something(a) on an object a of A type.
I would like to be able to tell what is the type N from inside the make_something function. Is there any way of achieving this without changing the template argument of make_something?
(In the following I've changed template<class> class E to template<class, class> class E because I used std::vector, which has two type template parameters, for E.)
I have no idea what you're doing, but to ask question about types, you need to write some traits.
I guess something like the following could be a starting point:
// to ask if something is an A at all
template<typename T>
struct IsAnA : std::false_type {};
template<template<class, class> class E, class N>
struct IsAnA<A<E, N>> : std::true_type {};
template<typename T>
constexpr bool is_an_A_v = IsAnA<T>::value;
// to query A's second template arg
template<typename T>
struct SecondTemplArgOfA {};
template<template<class, class> class E, class N>
struct SecondTemplArgOfA<A<E, N>> {
using type = N;
};
template<typename T>
using second_templ_arg_of_A_t = typename SecondTemplArgOfA<T>::type;
// make use of it
template<class T>
auto make_something(const T& t){
if constexpr (is_an_A_v<T>) {
using its2ndTemplArg = second_templ_arg_of_A_t<T>;
// so what?
} else {
// you've fed make_something with a non-A, so
// you can't get its second template arg
}
}
Demo.
Suppose I have a pointer to data member and I want to know if it's const or not. In other words:
struct S {
const int i; // this is const
int j;
};
In C++ I used to do something like this:
template<typename Class, typename Type, Type Class:: *>
struct is_const_data_member: std::false_type {};
template<typename Class, typename Type, const Type Class:: *Member>
struct is_const_data_member<Class, const Type, Member>: std::true_type {};
template<typename Class, typename Type, Type Class:: *Member>
void foo() {
const auto bar = is_const_data_member<Class, Type, Member>::value;
// ...
}
However, now there is the auto template parameter and template parameters list are much elegant:
template<auto Member>
void foo() {
// ...
}
In this case, the solo way I found to know if the data member point to something that is const is:
const auto bar = std::is_const_v<std::remove_reference_t<decltype(std::declval<Class>().*Member)>>;
However, it looks ugly to me and I feel like there must be a better way to do it.
Is there any other (shorter) solution for that?
You could change is_const_data_member to operate on a single type template parameter:
template<typename MemPtr>
struct is_const_data_member: std::false_type {};
template<typename Class, typename Type>
struct is_const_data_member<const Type Class::*>: std::true_type {};
Then, from template<typename Class, typename Type, Type Class:: *Member> void foo() you use it as
is_const_data_member<Type Class::*>::value
(Which, in my opinion, is slightly more intuitive.)
And from template<auto Member> void foo() you use it as
is_const_data_member<decltype(Member)>::value
You could also rewrite the trait to operate on an auto template parameter. But by using a type parameter you avoid unnecessary instantinations for different pointers of same type, which is supposedly a good thing.
How about something like this:
template <typename T>
struct is_const_data_member : std::false_type {};
template <typename C, typename T>
struct is_const_data_member<const T C::*> : std::true_type {};
template <auto T>
constexpr bool is_const_data_member_v = is_const_data_member<decltype(T)>::value;
And then, for example
struct Test
{
int a;
const int b;
};
bool x = is_const_data_member_v<&Test::a>;
bool y = is_const_data_member_v<&Test::b>;
working test here
Say I have a simple template like this:
template<typename T>
class A {};
And I want to specify that the type-parameter T is of some unrelated type X<U> where U is not known (or unspecifyable).
Is there a way how to express that as a concept?
Is there a way how to express that as a concept?
You don't need a concept, class template specialization works just fine in your case.
As an example, you can do this:
template<typename T>
class A;
template<typename U>
class A<X<U>> { /* ... */ };
This way, unless A is instantiated with a type of the form X<U> (where U is unknown), you'll get a compile-time error because the primary template isn't defined. In other terms, it won't work for all the types but X<U> (for each U), where the latter matches the class template specialization that has a proper definition.
Note that I assumed X is a known type. That's not clear from your question.
Anyway, if it's not and you want to accept types of the form X<U> for each X and each U, you can still do this:
template<typename T>
class A;
template<template<typename> class X, typename U>
class A<X<U>> { /* ... */ };
As a minimal, working example:
template<typename>
struct S {};
template<typename>
class A;
template<typename U>
class A<S<U>> {};
int main() {
A<S<int>> aSInt;
A<S<double>> aSDouble;
// A<char> aChar;
}
Both A<S<int>> and A<S<double>> are fine and the example compiles. If you toggle the comment, it won't compile anymore for A<char> isn't defined at all.
As a side note, if you don't want to use class template specialization and you want to simulate concepts (remember that they are not part of the standard yet and they won't be at least until 2020), you can do something like this:
#include<type_traits>
template<typename>
struct X {};
template<typename>
struct is_xu: std::false_type {};
template<typename U>
struct is_xu<X<U>>: std::true_type {};
template<typename T>
struct A {
static_assert(is_xu<T>::value, "!");
// ...
};
int main() {
A<X<int>> aXInt;
A<X<double>> aXDouble;
// A<char> aChar;
}
That is, given a generic type T, static assert its actual type by means of another structure (is_xu in the example) that verifies if T is of the form X<U> (for each U) or not.
My two cents: the class template specialization is easier to read and understand at a glance.
template <typename T, template <typename> class C>
concept bool Template = requires (T t) { {t} -> C<auto>; };
Now given a class template:
template <typename T>
struct X {};
a type template parameter can be constrained using:
template <typename T> requires Template<T, X>
class A {};
or:
template <Template<X> T>
class A {};
DEMO
This will work also for types derived from X<U>.
From a previous question:
Doing a static_assert that a template type is another template
Andy Prowl provided me with this code that allows me to static_assert that a template type is another template type:
template<template<typename...> class TT, typename... Ts>
struct is_instantiation_of : public std::false_type { };
template<template<typename...> class TT, typename... Ts>
struct is_instantiation_of<TT, TT<Ts...>> : public std::true_type { };
template<typename T>
struct foo {};
template<typename FooType>
struct bar {
static_assert(is_instantiation_of<foo,FooType>::value, ""); //success
};
int main(int,char**)
{
bar<foo<int>> b;
return 0;
}
This works great.
But this does not work for a subclass of foo<whatever>:
template<template<typename...> class TT, typename... Ts>
struct is_instantiation_of : public std::false_type { };
template<template<typename...> class TT, typename... Ts>
struct is_instantiation_of<TT, TT<Ts...>> : public std::true_type { };
template<typename T>
struct foo {};
template<typename FooType>
struct bar {
static_assert(is_instantiation_of<foo,FooType>::value, ""); //fail
};
//Added: Subclass of foo<int>
struct foo_sub : foo<int> {
};
int main(int,char**)
{
bar<foo_sub> b; //Changed: Using the subclass
return 0;
}
Can Andy Prowl's is_instantiation_of code be extended to allow for subclasses?
As KerrekSB wrote in his answer, an equally general extension of the solution you posted cannot be achieved.
However, if it is OK for you to give up a bit of genericity, you can write a type trait specific for foo (exploiting the fact that derived-to-base is one of the few conversions that are performed during type deduction):
#include <type_traits>
template<typename T>
struct foo {};
template<typename T>
constexpr std::true_type test(foo<T> const&);
constexpr std::false_type test(...);
template<typename T>
struct is_instantiation_of_foo : public decltype(test(std::declval<T>())) { };
You would then use it like so:
template<typename FooType>
struct bar {
static_assert(is_instantiation_of_foo<FooType>::value, "");
};
struct foo_sub : foo<int> {
};
int main(int,char**)
{
bar<foo_sub> b; // Will not fire
return 0;
}
Here is a live example.
This seems to work in many cases:
#include <iostream>
#include <utility>
template <typename T> struct foo { };
struct foo_sub : foo<int> { };
// A fallback function that will only be used if there is no other choice
template< template <typename> class X >
std::false_type isX(...)
{
return std::false_type();
}
// Our function which recognizes any type that is an instantiation of X or
// something derived from it.
template< template <typename> class X, typename T >
std::true_type isX(const X<T> &)
{
return std::true_type();
}
// Now we can make a template whose value member's type is based
// the return type of isX(t), where t is an instance of type T.
// Use std::declval to get a dummy instance of T.
template <template <typename> class X,typename T>
struct is_instantiation_of {
static decltype(isX<X>(std::declval<T>())) value;
};
template <typename FooType>
struct bar {
static_assert(is_instantiation_of<foo,FooType>::value,"");
};
int main(int,char**)
{
//bar<int> a; // fails the static_assert
bar<foo<int>> b; // works
bar<foo_sub> c; // works
return 0;
}
As noted by Yakk, one place that it doesn't work is if you have a class derived from multiple instantiations of foo, such as
struct foo_sub2 : foo<int>, foo<double> { };
You can't do that in C++11. You would essentially have to quantify over all class types and check if any of them is a base of the candidate.
There was a proposal in TR2 (which I hear is now defunct), possibly making it into C++14, to add traits std::bases and std::direct_bases which enumerate base classes of a given class, and thus effectively solve your problem (i.e. apply your existing trait to each base class).
GCC does provide this trait in <tr2/type_traits>, if that helps.
I am on my phone, so this might not work.
The goal is to use SFINAE and overloading to ask the question "is there a base class that matches this compile time traits question?"
template<template<typename>class Test>
struct helper {
static std::false_type test(...);
template<typename T, typename=typename std::enable_if< Test<T>::value >
static std::true_type test(T const&);
};
template<template<typename>class Test, typename T, typename=void>
struct exactly_one_base_matches :std::false_type {};
template<template<typename>class Test, typename T>
struct exactly_one_base_matches<Test,T,
typename std::enable_if<decltype(helper<Test>::test(std::declval<T>()))::value>::type>
:std::true_type {};
If that does not work for a generic test, one where test does pattern matching might. ... might need to be replaced. I cannot think of a way to deal with multiple bases that pass the test...
I think we can do better. There are three possible results from calling the above.
First, one parent or self matches the test. Second, it matches the catch-all. Third, it is ambiguous because it could pass the test in more than one way.
If we improve the catch-all to catch everything at low priority (Ts...&& maybe), we can make failure to compile a success condition.
Return true from SFINAE, true from match-one, and false from catch-all match-none.