What I'm looking for is a way to say: This is the same for all specializations:
template <typename T>
struct Foo {
using id_type = unsigned int; // this does not depend on T!
};
Foo::id_type theId; // Doesn't matter what the specialization is, id_type is always the same.
I want to access id_type without having to specify the specialization...
You can't have exactly what you are asking for. Foo is not a class. Foo<T> is a class, for any T.
You could have a non-template base that holds id_type
struct FooBase {
using id_type = unsigned int;
};
template <typename T>
struct Foo : FooBase{};
FooBase::id_type theId;
You could provide a default parameter for T
template <typename T = struct FooPlaceholder>
struct Foo {
using id_type = unsigned int; // this does not depend on T!
};
Foo<>::id_type theId;
However nothing stops me from writing an explicit specialisation of Foo that lacks (or redefines) id_type.
template <> struct Foo<MyType> { };
template <> struct Foo<MyOtherType> { int id_type = 42; };
Instead of id_type being a class (alias declaration) property, you could make it a stand-alone trait on a template template parameter:
#include <type_traits>
// Helper: compare with std::is_same but for
// template template parameter type arguments.
template <template <typename> typename, template <typename> typename>
struct is_same_primary_template : std::false_type {};
template <template <typename> typename TT>
struct is_same_primary_template<TT, TT> : std::true_type {};
template <template <typename> typename TT, template <typename> typename UU>
constexpr bool is_same_primary_template_v{
is_same_primary_template<TT, UU>::value};
template <typename T> struct Foo {};
template <template <typename> typename, typename Enable = void> struct id_type;
template <template <typename> typename TT>
struct id_type<TT, std::enable_if_t<is_same_primary_template_v<TT, Foo>>> {
using type = int;
};
// ...
template <template <typename> typename TT>
using id_type_t = typename id_type<TT>::type;
int main() { id_type_t<Foo> theId; }
This approach has one drawback, though. As the trait is specialized over specific types it couples these types with the implementation of the trait (as you want be very careful with the location of your specializations, w.r.t. the location of the primary template).
When writing a specialization, be careful about its location; or to make it compile will be such a trial as to kindle its self-immolation.
Just an extension based on Caleth's answer and the comment ensuing from that.
You can kind of protect yourself from "bad" specializations of Foo like this:
#include <iostream>
#include <type_traits>
//-------------------------------------------------------------------------
// from Caleth
struct FooBase
{
using id_type = unsigned int;
};
template <typename T>
struct Foo : FooBase
{
};
FooBase::id_type theId{};
//-------------------------------------------------------------------------
// specialization bypassing FooBase
template<>
struct Foo<char>
{
};
//-------------------------------------------------------------------------
// compile time check if someone mad a "bad" specialization, pre C++20
template<typename T>
void f(const Foo<T>& foo)
{
static_assert(std::is_base_of_v<FooBase, Foo<T>>);
}
//-------------------------------------------------------------------------
// C++20 concept to check for FooBase
template<typename T>
concept HasFooBase = std::is_base_of_v<FooBase, T>;
// only accepts types derived from FooBase
void g(const HasFooBase auto& foo)
{
}
//-------------------------------------------------------------------------
int main()
{
Foo<int> foo;
Foo<char> bar;
f(foo);
g(foo);
f(bar); // won't compile, error C2607: static assertion failed
g(bar); // won't compile, error C7602: 'g': the associated constraints are not satisfied
return 0;
}
Related
I have a template struct SFoo that contains a member struct SZug:
template <typename tTYPE>
struct SFoo
{
struct SZug {};
};
I have another struct SBar that takes a type parameter:
template <typename tTYPE>
struct SBar
{ /* stuff */ };
I would like to specialize SBar using SZug for the type parameter, like so:
template <typename tTYPE>
struct SBar<typename SFoo<tTYPE>::SZug>
{ /* different stuff */ };
This doesn't compile - LLVM outputs:
non-deducible template parameter 'tTYPE'
While a compiler could easily deduce this if it wished, I'm guessing it's just that the C++ spec would need to specifically cover this case.
Is there any way to achieve this?
(note: I'm currently working around it by moving SZug outside of SFoo and using a using declaration, but it's ugly.)
I am not sure I fully understood what you want to do, but you could try the following (it only requires adding a specific attributes to SZug:
template <typename tTYPE>
struct SFoo {
struct SZug {
// Add this to be able to obtain SFoo<T> from SFoo<T>::SZug
using type = tTYPE;
};
};
Then a small template to check if a type is a SFoo<T>::SZug:
template <typename tTYPE, typename Enabler = void>
struct is_SZug: public std::false_type { };
template <typename tTYPE>
struct is_SZug<tTYPE, typename std::enable_if<
std::is_same<tTYPE, typename SFoo<typename tTYPE::type>::SZug>{}
>::type>: public std::true_type { };
And a slight modification to the SBar template to enable the "specialization" if the type is a SZug:
template <typename tTYPE, typename Enabler = void>
struct SBar
{ static void g(); };
template <typename tTYPE>
struct SBar<tTYPE, typename std::enable_if<is_SZug<tTYPE>{}>::type>
{ static void f(); };
A little check:
void f () {
SBar<int>::g();
SBar<SFoo<int>::SZug>::f();
}
Note: You could also directly set SFoo<T> as the type attribute in SFoo<T>::SZug, you would simply need to change the second argument of std::is_same a little.
You can get the effect for which you're looking through the following (which prints out 0 1, BTW):
#include <type_traits>
#include <iostream>
namespace detail
{
struct SZugBase{};
}
template <typename tTYPE>
struct SFoo
{
struct SZug : public detail::SZugBase {};
};
template<typename tType, bool IsFoo>
struct SBarBase
{
int value = 0;
};
template<typename tType>
struct SBarBase<tType, true>
{
int value = 1;
};
template <typename tTYPE>
struct SBar : public SBarBase<tTYPE, std::is_convertible<tTYPE, detail::SZugBase>::value>
{ /* stuff */ };
int main()
{
SBar<int> b0;
SBar<SFoo<int>::SZug> b1;
std::cout << b0.value << " " << b1.value << std::endl;
}
Explanation
First, we give SZug a regular-class base:
namespace detail
{
struct SZugBase{};
}
template <typename tTYPE>
struct SFoo
{
struct SZug : public detail::SZugBase {};
};
Note the following:
SZugBase is not parameterized by anything, so it is easy to refer to it independently of the parameter of SFoo
SZugBase is in a detail namespace, so, by common C++ conventions, you're telling clients of your code to ignore it.
Now we give SBar two base classes, specialized on whether something is convertible to the non-template base of SZug:
template<typename tType, bool IsFoo>
struct SBarBase
{
int value = 0;
};
template<typename tType>
struct SBarBase<tType, true>
{
int value = 1;
};
Finally, we just need to make SBar a subclass of these bases (depending on the specialization):
template <typename tTYPE>
struct SBar : public SBarBase<tTYPE, std::is_convertible<tTYPE, detail::SZugBase>::value>
{ /* stuff */ };
Note that you don't specialize SBar here, you rather specialize the base classes. This effectively gives the same effect, though.
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....
template <template <typename> class F> struct call_me {};
template <typename T> struct maybe;
template <typename... T> struct more;
int main()
{
call_me<maybe> a; // ok
call_me<more> b; // error
}
I understand why call_me<more> fails. But I want to make it work.
Is there a workaround that doesn't involve changing call_me (or add an specialization to it)?
template <template <typename> class F> struct call_me {};
template <typename T> struct maybe;
template <typename... T> struct more;
template <template <class...> class F> struct just_one {
template <class A> using tmpl = F<A>;
};
int main()
{
call_me<maybe> a;
call_me<just_one<more>::tmpl> b;
}
Not exactly equivalent, but maybe close enough.
template <typename T> using onemore = more<T>;
int main()
{
call_me<onemore> b;
}
You could wrap more:
template <template <typename...> class Tmpl>
struct variwrap
{
template <typename> struct Dummy
{
template <typename ...Brgs>
struct rebind
{
typedef Tmpl<Brgs...> other;
};
};
};
Now you can say call_me<variwrap<more>::Dummy>, and the consumer can use F::rebind<Args...>::other to recover more<Args...>. Of course call_me has no way of knowing that F has the rebind member, so you'll need to add a specialization.
Yuck.
My question is w.r.t the following thread : specialize a member template without specializing its parent
I'm absolutely fine with the standard saying that it is illegal to do so. But i want to understand why is it illegal to do so? What would be impact had it been allowed?
Maybe because of something like this:
template <typename T>
struct foo
{
template <typename U>
struct bar
{
typedef U type;
};
};
template <typename T>
struct foo<T>::bar<int> // imaginary
{
typedef void type;
};
template <>
struct foo<float>
{
template <typename U>
struct bar
{
typedef U* type;
};
};
// is it void [foo<T>::bar<int>] or
// int* [foo<float>::bar<U>]?
typedef foo<float>::bar<int>::type ambiguous;
A sensible solution is to say "we'll make the entire thing explicit".