I tried to write a trait to check if a class has static function, but it always gives me the false. Could anyone tell me where is the problem?
#include <iostream>
template <template <typename...> class Trait, typename Ret, typename T>
struct is_detected : std::false_type
{
//This helps me to check that Get_t<A> is int
static_assert(std::is_same<Trait<T>, Ret>::value, "");
};
template <template <typename...> class Trait, typename T>
struct is_detected<Trait, Trait<T>, T> : std::true_type {};
class A {
public:
static int Get() {
std::cout << "I'm in get\n";
return 0;
}
};
template <typename T>
using Get_t = decltype(T::Get());
//template <typename T>
//using supports_Get = is_detected<Get_t, int, T>;
int main() {
std::cout << is_detected<Get_t, int, A>::value << std::endl;
return 0;
}
The following code works:
#include <iostream>
#include <type_traits>
namespace detail {
template <template <typename...> class Trait, typename V, typename T>
struct is_detected : std::false_type {};
template <template <typename...> class Trait, typename T>
struct is_detected<Trait, std::void_t<Trait<T>>, T> : std::true_type {};
}
class A {
public:
static int Get() {
std::cout << "I'm in get\n";
return 0;
}
};
template <typename T>
using Get_t = decltype(T::Get());
int main() {
std::cout << detail::is_detected<Get_t, void, A>::value << std::endl;
return 0;
}
Seems like there is not big difference between these examples.
Could anyone make me understand where is the problem of first code?
Note to C++17, 17.5.7.2 says, that
When a template-id refers to the specialization of an alias template,
it is equivalent to the associated type obtained by substitution of
its template-arguments for the template-parameters in the type-id of
the alias template. [ Note: An alias template name is never deduced. —
end note ]
This is one step process and subsequent template argument substitution never applies here.
Lets play with your first example:
template <template <typename> class Trait, typename Ret, typename T>
struct is_detected {};
struct A {
static int Get() {
return 0;
}
};
template <typename T>
using Get_t = decltype(T::Get());
template <typename T>
struct Get_t2 {
using Type = decltype(T::Get());
};
template <template <typename> class Trait, typename T>
struct is_detected<Trait, typename Trait<T>::Type, T> : std::true_type {};
template <template <typename> class Trait, typename T>
struct is_detected<Trait, Trait<T>, T> : std::true_type {};
Now you can see, that:
is_detected<Get_t2, int, A>::value // works, because it is normal type
is_detected<Get_t, int, A>::value // not, because it is alias
Interestingly your idea with void_t works because of C++17, 17.5.7.3
However, if the template-id is dependent, subsequent template argument
substitution still applies to the template-id
I recommend to use struct-based not alias-based templates whenever possible. Otherwise to make it work will be as hard as to kindle its self-immolation
Related
From How to check if an object is an instance of a template class of multiple template arguments in C++? I got the following trait to check whether a type is a particular template instantiation of a templated type of several template arguments:
template <typename T1, typename T2>
struct A
{
};
template <typename Type>
struct IsA: std::false_type
{
};
template <typename T1, typename T2>
struct IsA<A<T1, T2>> : std::true_type
{
};
How would one additionally add the condition that the second type T2 fulfills some other condition?
I tried doing
template <typename Type>
struct IsA: std::false_type
{
};
template <typename T1, typename T2, std::enable_if_t<SomeCondition<T2>::value>>
struct IsA<A<T1, T2>> : std::true_type
{
};
but am getting the error
error: template parameters not deducible in partial specialization:
You were on the right track:
#include <type_traits>
#include <iostream>
template <typename T1, typename T2>
struct A
{
};
template <typename Type, typename=void>
struct IsA: std::false_type
{
};
template <typename T1, typename T2>
struct IsA<A<T1, T2>, std::enable_if_t<std::is_same_v<T2, int>>>
: std::true_type
{
};
int main()
{
std::cout << IsA<int>::value << "\n";
std::cout << IsA<A<char, char>>::value << "\n";
std::cout << IsA<A<char, int>>::value << "\n";
return 0;
}
In this trivial example the "some condition" is just a std::is_same_v<T2, int>, only for the sake of an example.
My goal is to be able to compare templates i.e. write something like this
template<typename T>
struct template_type;
template<template <typename...> typename TTmpl, typename ...Ts>
struct template_type<TTmpl<Ts...>> { using type = TTmpl; }; // [1] this isn't valid C++
template<typename TRng, typename T>
auto find(TRng&& rng, const T& val)
{
using TTmpl = typename mcu::template_type<std::remove_const_t<std::remove_reference_t<TRng>>>::type;
if constexpr (std::is_same_v<TTmpl, std::set>) // [2] this isn't valid C++
{
return rng.find(val);
}
else
{
using namespace std;
return std::find(begin(rng), end(rng), val);
}
}
My second attemp was to use alias template and custom same function. Something like:
template<typename T>
struct template_type;
template<template <typename...> typename TTmpl, typename ...Ts>
struct template_type<TTmpl<Ts...>> { template<typename ...Us> using type = TTmpl<Us...>; };
template<template<typename...>typename T1, template<typename...> typename T2>
struct same : std::false_type {};
template<template<typename...>typename T>
struct same<T, T> : std::true_type {};
But this doesn't work either. The problem is that same<std::set, template_type<std::set<int>>::type>::value returns false, i.e. template class and it's template alias are different for some reason.
My third attemp was to write something like template_type_identity but I cannot figure out what should I use as identity:
template<template<typename...> typename T>
struct template_type_identity { using type = ???; }; // I could hardcode some constexpr ids for each template but I wanna generic solution
I am not sure if I understand what you want, also because I would use a different solution for your motivating case (sfinae on the presence of member find). Anyhow, this is how you can check if a given type is an instantiation of a template, provided the template has only type parameters:
#include <iostream>
#include <type_traits>
#include <set>
#include <vector>
template <template<typename...> typename T, typename C>
struct is_instantiation_of : std::false_type {};
template <template<typename...> typename T,typename ...P>
struct is_instantiation_of< T,T<P...>> : std::true_type {};
int main(){
std::cout << is_instantiation_of<std::set,std::set<int>>::value;
std::cout << is_instantiation_of<std::set,std::vector<int>>::value;
}
Output:
10
I've been trying to answer the question in the title, but I'm stumped. Basically, trying to see if there's a built-in way to tell the 'source' of a template instantiation, at least for classes. Here is an example of what I'd like to do:
template<class T>
class A { };
auto a = A<int>();
template<class T>
auto someFunction(T item) {
if(/* if type of a is from the templated class A */) {
// yep A<int> is 'from' A.
}
}
Is this possible, in some way such as this? I could use some saved value or inheritance shenanigans to get something similar, but I'd rather not.
Maybe with a custom type traits.
Something as follows
template <typename>
struct is_A : public std::false_type
{ };
template <typename T>
struct is_A<A<T>> : public std::true_type
{ };
// ...
template <typename T>
auto someFunction(T item) {
if( is_A<T>::value ) {
// yep A<int> is 'from' A.
}
}
Or, maybe, you want intercept non only A<T> but, generically, all types that are a template type, maybe with an undefined number of template arguments?
In this case you can try with something as follows
template <typename>
struct is_template : public std::false_type
{ };
template <template <typename...> class C, typename ... Ts>
struct is_template<C<Ts...>> : public std::true_type
{ };
Problem: this type traits intercepts all template types with types template arguments and only template arguments. But doesn't intercept, by example, std::integer_sequence<int, 0, 1, 2, 3, 4, 5> that receive a type and some non-type template parameter.
You can add other specializations for is_template, to intercept other cases, but you can't define a specialization that catch all template types (all combinations of template parameters).
Use a type trait
#include <type_traits>
#include <iostream>
template<class T>
class A { };
auto a = A<int>();
template <typename X>
struct is_from_A : std::false_type {};
template <typename T>
struct is_from_A<A<T>> : std::true_type {};
int main() {
std::cout << is_from_A<int>::value << "\n"; // 0
std::cout << is_from_A<A<int>>::value << "\n"; // 1
}
An other way, allowing derived class to match:
template <typename T>
std::true_type is_an_A_impl(A<T>*);
std::false_type is_an_A_impl(...);
template <typename T>
using is_a_A = decltype(is_an_A_impl(std::declval<T*>()));
How can I retrieve the template a type was originally instantiated from?
I'd like to do the following:
struct Baz{};
struct Bar{};
template <typename T>
struct Foo {};
using SomeType = Foo<Bar>;
template <typename T>
using Template = get_template<SomeType>::template type<T>;
static_assert(std::is_same<Foo<Baz>, Template<Baz>>::value, "");
I know I can achieve this through partial specialization, but this forces me to specialize get_template for every template I want to use it with:
template <typename T>
struct get_template;
template <typename T>
struct get_template<Foo<T>>
{
template <typename X>
using type = Foo<X>;
};
live example
Is there a way around this limitation?
You could do something like that, using a template template parameter (should work for templates with any number of type arguments):
template <typename T>
struct get_template;
template <template <class...> class Y, typename... Args>
struct get_template<Y<Args...>> {
template <typename... Others>
using type = Y<Others...>;
};
Then to get the template:
template <typename T>
using Template = typename get_template<SomeType>::type<T>;
As mentioned by #Yakk in the comment, the above only works for template that only have type arguments. You can specialize for template with specific pattern of type and non-type arguments, e.g.:
// Note: You need the first size_t to avoid ambiguity with the first specialization
template <template <class, size_t, size_t...> class Y, typename A, size_t... Sizes>
struct get_template<Y<A, Sizes...>> {
template <class U, size_t... OSizes>
using type = Y<U, OSizes...>;
};
...but you will not be able to specialize it for arbitrary templates.
DEMO (with Foo and std::pair):
#include <type_traits>
#include <map>
struct Bar{};
template <typename T>
struct Foo {};
using SomeType = Foo<Bar>;
template <typename T>
struct get_template;
template <template <class...> class Y, typename... Args>
struct get_template<Y<Args...>> {
template <typename... Others>
using type = Y<Others...>;
};
template <typename T>
using Template = typename get_template<SomeType>::type<T>;
static_assert(std::is_same<SomeType, Template<Bar>>::value, "");
static_assert(std::is_same<Foo<int>, Template<int>>::value, "");
using PairIntInt = std::pair<int, int>;
using PairIntDouble = std::pair<int, double>;
template <typename U1, typename U2>
using HopeItIsPair =
typename get_template<PairIntDouble>::type<U1, U2>;
static_assert(std::is_same<PairIntDouble, HopeItIsPair<int, double>>::value, "");
static_assert(std::is_same<PairIntInt, HopeItIsPair<int, int>>::value, "");
Not sure I got the question. Would this work?
#include<type_traits>
#include<utility>
template<typename V, template<typename> class T, typename U>
auto get_template(T<U>) { return T<V>{}; }
struct Baz{};
struct Bar{};
template <typename T>
struct Foo {};
using SomeType = Foo<Bar>;
template <typename T>
using Template = decltype(get_template<T>(SomeType{}));
int main() {
static_assert(std::is_same<Foo<Baz>, Template<Baz>>::value, "");
}
You can generalize even more (SomeType could be a template parameter of Template, as an example), but it gives an idea of what the way is.
I have a question about templates and it is in the code:
template<typename T>
struct foo {
T t;
};
template<typename FooType>
struct bar {
T t; //<- how to get T here (preferably without using typedef in foo)
};
Here's a generic template argument type extractor:
#include <tuple>
template <typename> struct tuplify;
template <template <typename...> class Tpl, typename ...Args>
struct tuplify<Tpl<Args...>>
{
using type = std::tuple<Args...>;
};
template <typename T, unsigned int N>
using get_template_argument
= typename std::tuple_element<N, typename tuplify<T>::type>::type;
Usage:
get_template_argument<std::vector<int>, 1> a; // is a std::allocator<int>
Or in your case:
get_template_argument<FooType, 0> t;
If I understood your question correctly, you could use template specialization as follows. Given your foo<> class template:
template<typename T>
struct foo {
T t;
};
Define a bar<> primary template and a corresponding specialization this way:
template<typename FooType>
struct bar;
template<typename T>
struct bar<foo<T>> {
T t; // T will be int if the template argument is foo<int>
};
Under the assumption that you are always supposed to instantiate bar by providing an instance of foo<> as the type argument, you can leave the primary template undefined.
The specialization will match the foo<T> pattern, thus giving you the type with which foo<> is instantiated in T.
Here is how you could test the validity of this approach with a simple program:
#include <type_traits>
int main()
{
bar<foo<int>> b;
// This will not fire, proving T was correctly deduced to be int
static_assert(std::is_same<decltype(b.t), int>::value, "!");
}
Here is the corresponding live example.
If you don't want or can't add a typedef to foo, you can additionally write an independent "extractor" template
template <typename T> struct ExtractT;
template <typename T> struct ExtractT<foo<T> > {
typedef T type;
};
and use it as
template<typename FooType>
struct bar {
typename ExtractT<FooType>::type t;
};
You can take that ExtractT one step further and decouple it from foo
template <typename T> struct ExtractT;
template <template <typename> class C, typename T> struct ExtractT<C<T> > {
typedef T type;
};
and so on until you reinvent something from Boost or C++11 standard library :) BTW, this feels like something that should already be available in form of a more generic solution....