Suppose I have a struct
template <typename Args>
struct MyStruct
{
};
But I only want to be able to instantiate this class with std::tuple instantiations, e.g.
Mystruct<std::tuple<>> a; // OK
Mystruct<std::tuple<int, int, double>> a; // OK
Mystruct<double> a; // FAIL
How can I do this?
This is fairly simple. Declare but not define a general template:
template<typename T> struct Mystruct;
then define a specialization:
template<typename... Ts>
struct Mystruct<std::tuple<Ts...>>
{
// stuff
};
In addition to krzaq's answer, to have a better error message, you might want to use static_assert
// The traits:
template <typename T>
struct is_a_tuple : std::false_type {};
template <typename ... Ts>
struct is_a_tuple<std::tuple<Ts...>> : std::true_type {};
// Your structure (for error message)
template <typename T>
struct MyStruct
{
static_assert(is_a_tuple<T>::value, "T should be a std::tuple");
};
// Your structure (for std::tuple)
template <typename ... Ts>
struct MyStruct<std::tuple<Ts...>>
{
// Your implementation
};
Related
I want to specialize a class template for variadic parameters:
template <typename... Ts>
struct TypeList
{
};
template <typename T>
class Foo
{
};
//Is next "specialization" even possible?
template <typename... Ts>
class Foo<TypeList<Ts...>>
{
};
Foo<TypeList<int, int>> i1{}; // OK
Foo<int, int> i2{}; // NOK: error: wrong number of template arguments (2, should be 1)
I want that users of Foo can choose between providing a TypeList or an explicit list of types.
What you can do, to allow both syntaxs:
template <typename ... Ts>
class Foo : Foo<TypeList<Ts...>>
{
};
// Specialization
template <typename ... Ts>
class Foo<TypeList<Ts...>>
{
// ...
};
I have defined the class
template <typename... Ts> struct Bar {
using inner_type = /* whatever */;
};
Now, I need to define a templated class Foo whose template parameters are some parameter pack, and a value of type Bar::inner_type instantiated for that parameter pack. Unfortunately I can't seem to be able to do it. If I define it this way:
template <Bar<Ts...>::inner_type SomeValue, typename... Ts> struct Foo { };
the compiler doesn't recognize Ts when it's used, since it hasn't see the parameter pack yet; but if I define it this way:
template <typename... Ts, Bar<Ts...>::inner_type SomeValue> struct Foo { };
the compiler sneers at my attempt to use a parameter pack before other template parameters.
So how can I do this?
Note: In case it matters, this failed for me with GCC 4.9.3.
You can partially specialize your struct:
template<typename...>
struct Bar { using inner_type = int; };
template <typename T, typename T::inner_type T>
struct Foo;
template <typename... Ts, typename Bar<Ts...>::inner_type SomeValue>
struct Foo<Bar<Ts...>, SomeValue> { };
int main() {
Foo<Bar<int>, 3> foo;
}
This way Ts parameter pack is deduced and Foo expects the second template parameter to be of type Bar<Ts...>::inner_type.
The best thing I could come up with:
template <class Inner, Inner Val, class... Args>
struct Foo {
static_assert(std::is_same<Inner, typename Bar<Args...>::inner_type>::value, "Wrong type");
};
You need to explicitly name the type.
does this solve the issue?
#include <type_traits>
using namespace std;
template <typename... Ts> class Bar {
public:
using inner_type = int;
};
template <typename... Ts> class Foo {
using bar_inner_type = typename Bar<Ts...>::inner_type;
static_assert(is_same<int, bar_inner_type>::value,"");
};
If I understood your problem correctly, you can do something like this:
template <typename... Ts> struct Bar {
using inner_type = /* whatever */;
};
template <typename... Ts> struct Foo {
using inner_type = typename Bar<Ts...>::inner_type;
};
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.
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...