I have code with the following structure:
template <typename T>
struct Foo
{
struct Bar
{
int data;
};
};
I want to write metafunctions which will tell me if a type is either Foo or Bar. The first one is easy:
template <typename T>
struct is_foo : boost::mpl::false_
{};
template <typename T>
struct is_foo<Foo<T> > : boost::mpl::true_
{};
...
BOOST_MPL_ASSERT(( is_foo<Foo<int> > ));
BOOST_MPL_ASSERT_NOT(( is_foo<int> ));
However, the same approach does not work for Bar:
template <typename T>
struct is_bar : boost::mpl::false_
{};
template <typename T>
struct is_bar<typename Foo<T>::Bar> : boost::mpl::true_
{};
This code is rejected by the compiler. GCC says:
main.cpp:38:8: error: template parameters not used in partial specialization:
main.cpp:38:8: error: âTâ
Oddly, clang will compile the code, but it issues a warning and the metafunction does not work (always false):
main.cpp:38:8: warning: class template partial specialization contains a template parameter that can not be deduced;
this partial specialization will never be used
struct is_bar<typename Foo<T>::Bar> : boost::mpl::true_
^~~~~~~~~~~~~~~~~~~~~~~~~~~~
main.cpp:37:20: note: non-deducible template parameter 'T'
template <typename T>
^
Is there a workaround for this issue? A c++11-specific solution would be fine.
Here's a hideously inelegant solution to my own question, using TTI (http://svn.boost.org/svn/boost/sandbox/tti):
First, add a dummy tag to Bar:
template <typename T>
struct Foo
{
struct Bar
{
typedef void i_am_bar;
int data;
};
};
Next, use TTI to check for that tag:
BOOST_TTI_HAS_TYPE(i_am_bar);
template <typename T>
struct is_bar : boost::tti::has_type_i_am_bar<T>
{};
...
BOOST_MPL_ASSERT(( is_bar<Foo<int>::Bar> ));
BOOST_MPL_ASSERT_NOT(( is_bar<Foo<int> > ));
BOOST_MPL_ASSERT_NOT(( is_bar<int> ));
Yucky to be sure, but it satisfies my use-case.
The problem is that T is part of the name of the type Foo<T>::Bar, but it's not part of the structure of the type.
A possible solution would be to encode T in the structure of the type:
template<typename Outer, typename Inner> struct Nested: public Inner {
using Inner::Inner;
};
template<typename T> struct Foo {
struct BarImpl {
int data;
};
using Bar = Nested<Foo<T>, BarImpl>;
};
template <typename T> struct is_bar: std::false_type {};
template <typename T, typename U> struct is_bar<Nested<Foo<T>, U>>:
std::is_same<typename Foo<T>::Bar, Nested<Foo<T>, U>> {};
Testing:
static_assert(is_bar<Foo<int>::Bar>::value, "!");
static_assert(!is_bar<Foo<int>>::value, "!");
static_assert(!is_bar<int>::value, "!");
compilers are correct, the simple and easy to understand explanation is: they just don't want to substitute all possible types T just to realise is there a nested type bar inside of given template. More precise explanation you may find in a 'classic' (and well known I hope) book about templates: "C++ Templates - The Complete Guide".
fortunately C++11 helps you to do it even better! :)
#include <type_traits>
template <typename T>
struct has_nested_bar
{
template <typename W>
struct wrapper {};
template <typename C>
static std::true_type check(
const wrapper<C>*
, const typename C::Bar* = nullptr
);
template <class C>
static std::false_type check(...);
constexpr static bool value = std::is_same<
decltype(check<T>(nullptr))
, std::true_type
>::type::value;
typedef std::integral_constant<bool, value> type;
};
this metafucntion would check if a given type has nested Bar type (as far as I understant it was initial purpise of your is_bar).
template <typename T>
struct Foo
{
struct Bar
{
int data;
};
};
struct Bar {};
int main()
{
std::cout << has_nested_bar<Foo<int>>::value << std::endl;
std::cout << has_nested_bar<Bar>::value << std::endl;
return 0;
}
will output:
zaufi#gentop /work/tests $ ./has-nested-bar
1
0
later you may combine this metafunction with your is_foo to check that nested Bar actually is inside of a Foo...
Related
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
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*>()));
The question inspired by recently arised question about extended std::is_base_of type trait.
Is there any technique, which allows us to distinguish between ordinary template parameter and template template parameter in modern C++ or its extensions (say, -std=gnu++1z clang++/g++)?
namespace details
{
template< /* ??? */ base >
struct is_derived_from;
template< typaneme base >
struct is_derived_from< base >
{
static std::true_type test(base *);
static std::false_type test(void *);
};
template< template< typename ...formal > base >
struct is_derived_from< /* ??? */ >
{
template< typename ...actual > // actual parameters must be here!
static std::true_type test(base< actual... > *);
static std::false_type test(void *);
};
} // namespace details
template< typename derived, /* ??? */ base >
using is_derived_from = decltype(details::is_derived_from< /* ? base< ? > */ >::test(std::declval< typename std::remove_cv< derived >::type * >()));
In positive case it allows us to make some of useful type traits much more powerfull (for example, STL's std::is_base_of).
I think it requires a language feature as a "generalized typenames", isn't it?
There can be only one set of template parameters for class templates, but you can use overloading constexpr function templates instead that dispatches to the appropriate class template. Take the is_derived_from trait in the linked question, with an extra SFINAE parameter so that you don't get a hard error when B is an inaccessible or ambiguous base:
#include <type_traits>
namespace detail
{
template <template <class...> class B, typename Derived>
struct is_derived_from
{
using U = typename std::remove_cv<Derived>::type;
template <typename... Args,
typename = std::enable_if_t<
std::is_convertible<U*, Base<Args...>*>::value>>
static auto test(B<Args...>*)
-> typename std::integral_constant<bool
, !std::is_same<U, B<Args...>>::value>;
static std::false_type test(void*);
using type = decltype(test(std::declval<U*>()));
};
using std::is_base_of; // may want to use is_convertible instead to match
// the semantics of is_derived_from
}
template <template <class...> class B, typename Derived>
constexpr bool my_is_base_of() { return detail::is_derived_from<B, Derived>::type::value; }
template <class B, typename Derived>
constexpr bool my_is_base_of() { return detail::is_base_of<B,Derived>::value; }
struct B {};
struct D : B {};
template<class ...>
struct B2 {};
struct D2 : B2<int, double> { };
int main() {
static_assert(my_is_base_of<B2, D2>(), "Oops");
static_assert(my_is_base_of<B, D>(), "Oops");
static_assert(my_is_base_of<B2<int, double>, D2>(), "Oops");
static_assert(!my_is_base_of<B, D2>(), "Oops");
}
Demo.
You asked:
Is there any technique, which allows us to distinct between ordinary template parameter and template template parameter in modern C++ or its extensions (say, -std=gnu++1z clang++/g++)?
Seems to me like you need something like:
template <typename T>
struct is_template_template : public std::false_type
{
};
template <typename T1, template <typename T> class T2>
struct is_template_template<T2<T1>> : std::true_type
{
};
Example Program
#include <iostream>
template <typename T>
struct is_template_template : public std::false_type
{
};
template <typename T1, template <typename T> class T2>
struct is_template_template<T2<T1>> : std::true_type
{
};
template <typename T> struct A {};
struct B {};
int main()
{
std::cout << std::boolalpha;
std::cout << is_template_template<A<int>>::value << std::endl;
std::cout << is_template_template<B>::value << std::endl;
return 0;
}
Output:
true
false
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.
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....