I am trying to wrap my head around template specialization, and I am a bit confused (perhaps not understanding what typename actually is, or what the compiler expects)
Example 1 (Compiles):
template <typename A, typename... Args>
class Example
{
public:
Example(){}
virtual ~Example(){}
};
template <typename A, typename... Args>
class Example<A, int, Args...>{
Example(){}
virtual ~Example(){}
};
Example 2 (Compiles):
template <typename A, int, typename... Args>
class Example
{
public:
Example(){}
virtual ~Example(){}
};
template <typename A, typename... Args>
class Example<A, 2, Args...>{
Example(){}
virtual ~Example(){}
};
Example 3 (Fails):
template <typename A, typename... Args>
class Example
{
public:
Example(){}
virtual ~Example(){}
};
template <typename A, typename... Args>
class Example<A, 2, Args...>{
Example(){}
virtual ~Example(){}
};
The error is:
error: type/value mismatch at argument 2 in template parameter list for ‘template class Example
Questions:
First, I am new to generic programming, and I hope I am asking the right questions. The compiler spec terminology is still a bit foreign to me.
What is happening? Is the compiler trying to treat the constant as
typename?
If typename can be specialized as int, and int can be specialized as 2, why can't a typename be specialized as 2?
What would be a "proper" method to specialize the class with int or enum?
Am I asking the right questions?
Thank you
EDIT/Solution:
After I understood what is going on (from Yakk's explanation), here is how my final solution looks like. I read somewhere by one of the C++ guru's that "You can solve any problem by adding another layer of abstraction". Now I know what that means :D
enum ETypes
{
UNKNOWN = 0,
INT = 1,
FLOAT = 2,
STRING = 3,
FUNC = 4,
};
// This is to use the ETypes as a type.
// Note that T is not a type, hence use it as RHS
template<ETypes T>
class ETypeName
{
public:
ETypes type = T;
};
// The example
template <typename A, typename... Args>
class Example
{
private:
Example(); // Hide the constructor as private
// to generate compilation error
virtual ~Example(){}
};
// LOOK! We can use the Enum to specialize the class.
template <>
class Example<ETypeName<ETypes::INT>>{
public:
ETypes mType;
Example():mType(ETypes::INT){}
virtual ~Example(){}
};
And in main():
Example<ETypeName<ETypes::INT>> x;
// This can't happen. Private constructor. Not specialized yet
// Example<ETypeName<ETypes::FLOAT>> x1;
A primary specialization looks like this:
template <typename A, typename... Args>
class Example
When you type Example<stuff goes here>, it is always matched against the primary specialization's <typename A, typename... Args> argument list.
This is a completely different beast:
template <typename A, typename... Args>
class Example<A, int, Args...>
This is a secondary specialization. Here,
template <typename A, typename... Args>
is not the argument list, but rather the deduction list.
The argument list is:
class Example<A, int, Args...>
here. What is between the <> is only used to pattern match against arguments passed to the primary specialization.
Types and non-type template parameters are different things. The primary specialization details what arguments are type, and what arguments are non-type.
Once they have matched against the primary, each of the secondary specializations are pattern matched against the arguments. Each viable candidate is examined, and a reasonably complex system is used to determine which is "more specialized", the rules of which I won't go into here.
What is happening? Is the compiler trying to treat the constant as typename?
Yes.
If typename can be specialized as int, and int can be specialized as 2, why can't a typename be specialized as 2?
A template parameter starting with typename requires a type as argument. In your second example, the second template parameter is int, not typename something. As such, it does not expect a type as argument, but an actual int value.
Your third example uses a template definition that only expects typename template parameters, but you try and give it an int value as argument. This is why you get the error.
What would be a "proper" method to specialize the class with int or enum?
Not sure I understand the meaning of the question properly. The proper way to specialize your template for instances using an int second template parameter is your second template definition.
Related
This looks like an issue in clang (I've already opened a bug here), but I'd like to be sure that I'm not doing a mistake.
Consider the following code:
#include <type_traits>
#include <cstddef>
template<std::size_t N, std::size_t M, std::enable_if_t<not (N>M)>* = nullptr> // (1)
struct S: public S<N+1, M> { };
template<std::size_t N>
struct S<N, N> { };
int main() {
S<0, 1> c{};
}
It fails to compile with the following error:
8 : error: non-type template argument specializes a template parameter with dependent type 'std::enable_if_t M)> *' (aka 'typename enable_if M), void>::type *')
struct S { };
The same code works as expected using the following line instead of (1):
template<std::size_t N, std::size_t M, typename = std::enable_if_t<not (N>M)>>
The SFINAE expression is almost the same. It is based on a specialization of std::enable_if_t and I would expect the same result (success or failure) for both of the examples.
Are my expectations wrong?
Note that GCC works fine in either cases.
I think this is a gcc bug actually, as a result of [temp.class.spec]:
The type of a template parameter corresponding to a specialized
non-type argument shall not be dependent on a parameter of the
specialization. [ Example:
template <class T, T t> struct C {};
template <class T> struct C<T, 1>; // error
template< int X, int (*array_ptr)[X] > class A {};
int array[5];
template< int X > class A<X,&array> { }; // error
—end example ]
In your example, the type of the 3rd template parameter is dependent on a parameter. When you swap it to typename = std::enable_if_t<...>, then this rule no longer applies.
Note: is there any reason to use SFINAE here anyway, as opposed to static_assert-ing?
I've stumbled upon something I can't get through since last week...
Having this:
template<typename> struct fx;
template<typename R, typename...Args>
struct fx<R(Args...)>
{
virtual R operator()(const Args & ...x) const = 0;
};
and this:
template<typename> struct fx_err;
// I feel here something is wrong, but I can't figure it out.
template<template<typename> class F, typename R, typename... Args>
struct fx_err< F<R(Args...)> > : fx<R(R,Args...)>
{
using fx_type = F<R(Args...)>;
fx_type f;
R operator ()(const R &y, const Args & ...x) const override
{
return y - f(x...);
}
};
and this:
struct example_fun : fx<int(int,int,int)>
{
int operator() (const int &a, const int &b, const int &c) const override
{
return a * b * c;
}
};
when finally I try to use it like:
fx_err<example_fun> example;
int err = example(24,2,3,4);
compiler throws error: 'example has incomplete type'.
Something similar works only if I do not specialize fx_err and use pointer to fx_type functor instead, but then I need to add constructor to grab the pointer itself which is not something I want.
It's very frustrating. What's wrong with this? Is it possible what I'm trying to achieve? Can anybody help?
Thanks in advance.
UPDATE:
here's example code to play around with for those willing to experiment with this example: http://pastebin.com/i3bRF8tB
The problem on line:
fx_err<example_fun> example;
is caused by the fact that example_fun is "passed" to fx_err, which selects the declaration:
template<typename> struct fx_err;
which is an incomplete type.
The specialization you provide:
// I feel here something is wrong, but I can't figure it out.
template<template<typename> class F, typename R, typename... Args>
struct fx_err< F<R(Args...)> > : fx<R(R,Args...)>
{ ... }
cannot be selected, because example_fun is not a template class as required by:
template<typename> class F
Avoid template template parameters if you can. They add more complexity, and less flexibility, than you probably want.
It looks like you're trying to match the form of the fx base class against its derived class. Partial specialization requires exact matches, it won't slice to the base class. And even if it did, this member would be of abstract class type:
using fx_type = F<R(Args...)>;
fx_type f; // same as fx<R(Args...)> which is abstract
The solution is to preserve the derived class, and tell the partial specialization how to find the base. Then the partial specialization can do pattern matching on the base class.
template<typename derived, typename base = typename derived::fx>
struct fx_err;
template<typename derived, template<typename> class F, typename R, typename... Args>
struct fx_err< derived, F<R(Args...)> > : F<R(R,Args...)>
Live solution: http://coliru.stacked-crooked.com/a/870172bcad0a9034
Of course, finding the base class by typename derived::fx sort-of begs the question of what base class template was used. In theory, you could have several templates of the same name, or derived could have a member typedef my_base fx; instead of inheriting from an fx specialization.
More likely, though, you don't need template<typename> class F at all.
template<typename derived, typename base = typename derived::fx>
struct fx_err;
template<typename derived, typename R, typename... Args>
struct fx_err< derived, fx<R(Args...)> > : fx<R(R,Args...)>
fx_err<example_fun> does not match your partial specialization because example_fun is not of the form F<R(Args...)>. It inherits a type of that form, but that's not the same thing.
When selecting specializations for class templates, implicit conversions aren't considered. Therefore example_fun isn't seen as a fx<...> by the compiler when matching the specializations and the primary (undefined) template is chosen over the other specialization.
To solve this you can expose an alias for the base class in the derived class:
struct example_fun : fx<int(int,int,int)>
{
using type = fx<int(int,int,int)>;
};
And now use this alias at the declaration site:
fx_err<example_fun::type> example;
int err = example(24,2,3,4);
You can even use a macro to avoid repeating the base class name:
template<class T> struct tag { using type = T; };
#define BASE_TAG(B) B, public tag<B>
struct example_fun : BASE_TAG(fx<int(int, int, int)>) {
// ...
};
Your basic problem is that template type pattern matching does not work like function overload template pattern matching. Inheritance is ignored, only the type passed in is pattern matched against.
So struct foo: some_template<some_args...> does not match some_template<some_args...> during template type pattern matching for the purpose of figuring out which specialization to use.
This lets us work with types as values in functions:
template<class T>struct tag{using type=T;};
template<class Tag>using type_t=typename Tag::type;
Now function template pattern matching works more like what you seem to be expecting:
template<template<class...>class Z, class...Args>
constexpr tag<Z<Args...>> get_template( Z<Args...>const& ) { return {}; }
takes a single argument, does template function pattern matching and deduction against it. This will look at parents of the type passed in. It tries to match Z<Args...> for some template Z.
It returns a tag<Z<Args...>>, which is a stateless type that just stores the type we need. We can then feed the above through an alias to extract that template expansion:
template<class T>
using get_template_t = type_t<decltype(get_template(std::declval<T>()))>;
which is most of the way there.
Next, we need some SFINAE helper magic:
template<class...>struct voider:tag<void>{};
template<class...Ts>using void_t=type_t<voider<Ts...>>;
std::void_t is C++14, and takes a bunch of types and throws them away, returning void instead. I do it in 2 lines because some compilers fail on the one-line version.
Ok, now we attack fx_err:
template<class,class=void> struct fx_err;
the second class=void lets us do FSINAE work. We start with your
template<template<class...>class F, class R, class...Args>
struct fx_err< F<R(Args...)>, void > : fx<R(R,Args...)>
{
using fx_type = F<R(Args...)>;
fx_type f;
R operator ()(const R &y, const Args & ...x) const override {
return y - f(x...);
}
};
and we also do this:
template<class T>
struct fx_err< T, void_t<get_template_t<T>>> : fx_err<get_template_t<T>>
{};
which I think should work. If it doesn't, we just need to add a test to exclude the direct T<F(Args...)> case from this specialization.
I have been playing around with SFINAE applied to the "has_member" type of structs as described here.
So I was trying to use some of the features of c++11 to make these solutions simpler. Having some problems with checking for existence of an enum, but I can't seem to locate my logic error here. Code is:
#include<iostream>
template<typename T >
struct has_enum_name
{
private:
// ***** this is the line that isn't working *****
// If U has an enum "name", then I expect is_enum to return true…
// Then I expect enable_if<>::type to be std::true_type…etc. This isn't what happens.
template<typename U> static auto test(int) -> decltype(
std::enable_if<std::is_enum< typename U::name >::value, std::true_type>::type() );
template<typename U> static auto test(...) -> std::false_type;
public:
static constexpr bool value =
!(std::is_same<decltype(test<T>(0)),std::false_type>::value);
static constexpr bool is_enum()
{
return std::is_enum<typename T::name>::value;
}
};
template<typename T >
struct has_enum_name_2
{
private:
template<typename U> static auto test(int) -> decltype(
std::enable_if<true, std::true_type>::type() );
template<typename U> static auto test(...) -> std::false_type;
public:
static constexpr bool value =
!(std::is_same<decltype(test<T>(0)),std::false_type>::value);
};
struct Foo
{
enum class name
{
enum1,
enum2
};
};
int main()
{
std::cout<<"std::is_enum<Foo::name>::value = "<<std::is_enum<Foo::name>::value<<std::endl;
std::cout<<"has_enum_name<Foo>::value = "<<has_enum_name<Foo>::value<<std::endl;
std::cout<<"has_enum_name<Foo>::is_enum() = "<<has_enum_name<Foo>::is_enum()<<std::endl;
std::cout<<"has_enum_name_2<Foo>::value = "<<has_enum_name_2<Foo>::value<<std::endl;
}
Running this using gcc 4.9.2 gives
$ ./a.out
std::is_enum<Foo::name>::value = 1
has_enum_name<Foo>::value = 0
has_enum_name<Foo>::is_enum() = 1
has_enum_name_2<Foo>::value = 1
The first output line verifies that the std::is_enum works correctly for the Foo::name enum.
The second line outputs the results of struct constexpr "has_enum_name::value". I am just trying to use std::enable_if in conjunction with std::is_enum to make the decltype return something…in this case std::true_type(). As you can see, the output returns false…so the line:
template<typename U> static auto test(int) -> decltype(
std::enable_if<std::is_enum< typename U::name >::value, std::true_type>::type() );
isn't working out like i think it should. I think that is_enum should return true, which means that enable_if should return true_type, and decltype should return true_type().
Next output is just a check to see if std::is_enum<> is working correctly in the struct…it is.
Next output is just a copy of the "has_enum_name" struct, but I replaced the is_enum<> in the suspect line with a hard coded "true"…and it works correctly.
So for some reason, it appears that is_enum is not working returning true. Any ideas what I am doing wrong here?
When you encounter a problem like this, let the compiler help you. Remove the (...) overload to force a compilation error, and see what GCC tells you:
test.cc: In instantiation of ‘constexpr const bool has_enum_name<Foo>::value’:
test.cc:51:71: required from here
test.cc:18:33: error: no matching function for call to ‘has_enum_name<Foo>::test(int)’
!(std::is_same<decltype(test<T>(0)),std::false_type>::value);
^
test.cc:18:33: note: candidate is:
test.cc:12:36: note: template<class U> static decltype (std::enable_if<std::is_enum<typename U::name>::value, std::integral_constant<bool, true> >::type()) has_enum_name<T>::test(int) [with U = U; T = Foo]
template<typename U> static auto test(int) -> decltype(
^
test.cc:12:36: note: template argument deduction/substitution failed:
test.cc: In substitution of ‘template<class U> static decltype (std::enable_if<std::is_enum<typename U::name>::value, std::integral_constant<bool, true> >::type()) has_enum_name<T>::test(int) [with U = Foo]’:
test.cc:18:33: required from ‘constexpr const bool has_enum_name<Foo>::value’
test.cc:51:71: required from here
test.cc:13:83: error: dependent-name ‘std::enable_if<std::is_enum<typename T::name>::value, std::integral_constant<bool, true> >::type’ is parsed as a non-type, but instantiation yields a type
std::enable_if<std::is_enum< typename U::name >::value, std::true_type>::type() );
^
test.cc:13:83: note: say ‘typename std::enable_if<std::is_enum<typename T::name>::value, std::integral_constant<bool, true> >::type’ if a type is meant
That's exactly what's wrong: you merely forgot to add typename. T::type() could be valid if type is a non-type: it could just be a function call. That's how the compiler is parsing it.
By the way, decltype(typename T::type()) seems somewhat pointless unless you're specifically trying to check whether the type is default-constructible, and that's not what you're going for here. You can simply use typename T::type directly, like so:
template<typename U> static auto test(int) ->
typename std::enable_if<std::is_enum<typename U::name>::value, std::true_type>::type;
and if you had tried that without the typename, you would've immediately got a useful error message.
Your initialisation of value is also needlessly complicated, since you already know you're dealing with false_type or true_type: both of those have a value member you can use.
static constexpr bool value = decltype(test<T>(0))::value;
I tried the following code but it gives:
main.cpp:29:22: error: aggregate 'pop<std::tuple<int, char, float> > p' has incomplete type and cannot be defined
What am I missing?
template <typename T>
struct pop;
template <typename E, typename... Ts>
struct pop<tuple<Ts..., E>> {
using result = tuple<Ts...>;
};
tuple<int, char, float> t;
typename pop<decltype(t)>::result p;
If Ts... must be at the end in a type list, why does it work in this example from http://en.cppreference.com/w/cpp/language/parameter_pack:
template<class A, class B, class...C> void func(A arg1, B arg2, C...arg3)
{
container<A,B,C...> t1; // expands to container<A,B,E1,E2,E3>
container<C...,A,B> t2; // expands to container<E1,E2,E3,A,B>
container<A,C...,B> t3; // expands to container<A,E1,E2,E3,B>
}
tuple<Ts..., E> is a non-deduced context. [temp.deduct.type]/9:
If P has a form that contains <T> or <i>, then each argument Pi of the respective template argument list P is compared with the corresponding argument Ai of the corresponding template argument list of A. If the template argument list of P contains a pack expansion that is not the last template argument, the entire template argument list is a non-deduced context.
That means that your partial specialization is never matched.
With C++14, one could use
template <class T, class=std::make_index_sequence<std::tuple_size<T>::value-1>>
struct pop;
template <typename Tuple, std::size_t... indices>
struct pop<Tuple, std::index_sequence<indices...>>
{
using type = std::tuple<std::tuple_element_t<indices, Tuple>...>;
};
template <typename T>
using pop_t = typename pop<T>::type;
Such that
using t = std::tuple<int, char, float>;
static_assert( std::is_same<pop_t<t>, std::tuple<int, char>>{}, "" );
Compiles.
Demo.
Ts... must be the last element of a type list if you want it to be deduced. tuple<Ts...,E> will not deduce Ts... to be all but the last, but rather never match anything.
Getting rid of the last argument is a bit tricker. live example:
#include <iostream>
#include <tuple>
#include <iostream>
namespace details {
template<class Lhs, class Rhs>
struct pop_helper;
template<template<class...>class Tup, class L0, class...Lhs, class...Rhs>
struct pop_helper<Tup<L0,Lhs...>, Tup<Rhs...>>:
pop_helper<Tup<Lhs...>, Tup<Rhs...,L0>>
{};
template<template<class...>class Tup, class L0, class...Rhs>
struct pop_helper<Tup<L0>, Tup<Rhs...>> {
using type=Tup<Rhs...>;
};
}
template <typename T>
struct pop;
template<template<class...>class Tup, class...Ts>
struct pop<Tup<Ts...>>:
details::pop_helper<Tup<Ts...>,Tup<>>
{};
template<typename T>
using pop_t=typename pop<T>::type;
std::tuple<int, char, float> t;
typedef pop_t<decltype(t)> p;
int main() {
p x = std::make_tuple( 7, 'x' );
std::cout << std::get<0>(x) << std::get<1>(x) << std::tuple_size<p>{} << "\n";
}
pop_helper moves the types over one at a time to the right hand side, until there is only one type left on the left hand side. Then it returns the right hand side type.
pop just passes the tuples over.
I used template<class...>class Tup instead of std::tuple, because why not support almost every template instead of just std::tuple?
pop_t gets rid of the annoying typename spam at point of use.
I use the inhertance-as-type-map-forwarding pattern, which saves on typing. With a type-map, the structure:
template<class X>
struct bob: foo<X> {};
can be read as bob<X> is foo<X>. The alternative is the more verbose
template<class X>
struct bob {
using type = typename foo<X>::type;
};
expanding variardic type lists is different than matching them. When it was designed, matching was kept simple in order to make compiler vendors be able to implement the feature. There may even be thorny issues beyond "it is tricky" down that path.
Another C++11 way to skin this cat:
#include <tuple>
template<class Tuple>
struct pop;
template<>
struct pop<std::tuple<>>
{
using type = std::tuple<>;
};
template<typename T1>
struct pop<std::tuple<T1>>
{
using type = std::tuple<>;
};
template<typename First, typename... Rest>
struct pop<std::tuple<First,Rest...>>
{
using type =
decltype(std::tuple_cat(
std::declval<std::tuple<First>>(),
std::declval<typename pop<std::tuple<Rest...>>::type>()));
};
// Test...
static_assert(std::is_same<pop<std::tuple<>>::type,std::tuple<>>::value,"");
static_assert(std::is_same<pop<std::tuple<int>>::type,std::tuple<>>::value,"");
static_assert(
std::is_same<pop<std::tuple<int,char>>::type,std::tuple<int>>::value,"");
static_assert(
std::is_same<pop<std::tuple<int,char,float>>::type,
std::tuple<int,char>>::value,"");
static_assert(
std::is_same<pop<std::tuple<int,char,float,double>>::type,
std::tuple<int,char,float>>::value,"");
This is the solution I had come up with:
template <typename T>
struct pop;
template <typename E, typename... Ts>
struct pop<std::tuple<E, Ts...>> {
using type = decltype(tuple_cat(
declval<tuple<E>>(),
declval<typename pop<tuple<Ts...>>::type>()
));
};
template <typename E>
struct pop<std::tuple<E>> {
using type = std::tuple<>;
};
I looked a bit into Eric Nieblers range library https://github.com/ericniebler/range-v3/ and there (/include/range/v3/utility/concepts.hpp, line 36) I found code of the form
constexpr struct valid_expr_t
{
template<typename ...T>
true_ operator()(T &&...) const;
} valid_expr {};
I am confused to the second scope/braces after valid_expr. What is the meaning of the whole construct. Is this even a struct definition? The syntax seems not allowed in C++98. What can go into these second pair of braces?
It's the C++11 uniform initialization syntax, and it simply initializes the valid_expr object.
It's like doing
struct valid_expr_t
{
template<typename ...T>
true_ operator()(T &&...) const;
};
constexpr valid_expr_t valid_expr {};