Template Meta-programming, with Variadic Templates: compiler error - c++

I'm attempting variadic template meta programming for the first time and consistently getting a compiler error that I have not been able to track down.
I'm following the "tuple" example on this page (although I'm calling my object an ItemSet)
The ItemSet part compiles just fine:
template<typename...Ts> class ItemSet { };
template<typename item_T, typename ...Ts>
class ItemSet<item_T, Ts...> : public ItemSet<Ts...>
{
public:
ItemSet(item_T t, Ts... item_types) : ItemSet<Ts...>(item_types...), tail(t) { }
protected:
item_T tail;
};
template <typename...M> struct type_holder;
template<typename T, typename ...M>
struct type_holder<0, ItemSet<T, M...>>
{ // ERROR: M parameter pack expects a type template argument.
typedef T type;
};
template <int k, typename T, typename ...M>
struct type_holder<k, ItemSet<T, M...>>
{
typedef typename type_holder<k - 1, ItemSet<M...>>::type type;
};
int main()
{
ItemSet<int, string, string, double> person(0, "Aaron", "Belenky", 29.492);
}
However, in the commented out code, I get the compiler error at the declaration of type_holder.
I've tried a bunch of variations on the same syntax, but always with the same issue.
I'm using Microsoft Visual Studio 2013, which is supposed to have full support for Template programming and Variadic Templates.
Do you understand what the compiler error is, and can you explain it to me?

The immediate problem is that you are defining a specialization of type_holder without a general template. In addition, there is a simple typo (typeholder instead of type_holder). Fixing these two issues makes it compile with other compilers:
template <int, typename T>
struct type_holder;
template <int k, typename T, typename ...M>
struct type_holder<k, ItemSet<T, M...>>
{
typedef typename type_holder<k - 1, ItemSet<M...>>::type type;
};
template<class T, class ...M>
struct type_holder<0, ItemSet<T, M...>>
{
typedef T type;
};
The error emitted by the compilers you used isn't particular helpful. I'd recommend keeping a few C++ compilers around just to test template code with (I'm typically using gcc, clang, and Intel's compiler).

Related

Inconsistent compiler behavior

I'm seeing some inconsistencies between gcc, clang and msvc 2015/2017. The following fails on msvc but not gcc or clang. These meta-functions should return the type of a parameter in a function signature type:
template <int _ParamNum, typename _Ty> struct _get_parameter;
template <typename _ReturnT, typename _HeadT, typename ... _TailT>
struct _get_parameter<0, _ReturnT(_HeadT, _TailT...)>
{
using type = _HeadT;
};
template <int _ParamNum, typename _ReturnT, typename _HeadT, typename ... _TailT>
struct _get_parameter<_ParamNum, _ReturnT(_HeadT, _TailT...)>
{
using type =
typename _::template _get_parameter<_ParamNum - 1, _ReturnT(_TailT...)>::type;
};
called with something like:
using param_type = typename _get_parameter<0, decltype(printf)>::type;
I'm inclined to think its an MSVC bug but I hope somebody can eyeball a problem with the code instead.
Thanks in advance
Edit:
Well I stand corrected: http://rextester.com/XZEL74804 it seems to be some strange interaction with the larger code base.

VS2015 C++ : unable to match function definition to an existing declaration

I try to make NCORR library work on VS2015 C++. The library was originally written on Linux.
As part of the library there is code similar to
#include <chrono>
template<typename T_container>
struct container_traits {
typedef typename T_container::container nonconst_container;
};
template <typename T_container>
class base_region {
public:
typedef typename container_traits<T_container>::nonconst_container nonconst_container;
template<typename T_container2>
typename std::enable_if<std::is_same<typename container_traits<T_container2>::nonconst_container, typename nonconst_container>::value, base_region&>::type operator=(const base_region<T_container2>&);
};
template <typename T_container>
template <typename T_container2>
typename std::enable_if<std::is_same<typename container_traits<T_container2>::nonconst_container, typename base_region<T_container>::nonconst_container>::value, base_region<T_container>&>::type base_region<T_container>::operator=(const base_region<T_container2>& reg) {
return *this;
}
int main()
{
return 0;
}
When I try to compile the code on VS2015 win32 console application, I get
C2244 'base_region<T_container>::operator =': unable to match function definition to an existing declaration ConsoleApplication5
I think the problem is typename nonconst_container in declaration vs typename base_region<T_container>::nonconst_container in definition.
Do you know what is wrong and how can I make the code work?
First, typename nonconst_container is ill-formed. typename is only allowed before a qualified name.
MSVC seems to have problems matching nonconst_container in the class template definition and the typename base_region<T_container>::nonconst_container in the out-of-class member function definition.
A workaround (which is also shorter) is to use a trailing-return-type:
template <typename T_container>
template <typename T_container2>
auto base_region<T_container>::operator=(const base_region<T_container2>& reg)
-> typename std::enable_if<std::is_same<typename container_traits<T_container2>::nonconst_container,
nonconst_container>::value, base_region&>::type
// ^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^
{
//...
}
Everything after base_region<T_container>::operator= gets looked up just like a name used in the member function body (in particular, it will first look at class members), so you can just write nonconst_container and base_region&, and happily sidestep the MSVC bug.

Using non-type template argument during partial specialization

Consider the following structs:
//Implementations provided elsewhere
struct A { A(int i, double d, std::string s); /* ... */ };
struct B { B(double d1, double d2); /* ... */ };
I have two conversion classes whose template signatures look like:
TupleAs< A, int, double, std::string > via1 { ... };
ArrayAs< B, double, 2 > via2 { ... };
Predictably, TupleAs converts a triplet of int,double, and std::string values into an object of type A. Similarly, ArrayAs converts a pair of two double values into an object of type B. (And yes, there are reasons why I cannot call the A and B constructors directly.)
Improving the syntax
I would like to change the syntax so I can do the following:
TupleAs< A(int,double,std::string) > via1 { ... };
ArrayAs< B(double,2) > via2 { ... };
which, I think, is more descriptive of a conversion process. The TupleAs template declaration and corresponding partial specialization would look like this:
template <typename T> struct TupleAs;
template <typename T, typename ... Args>
struct TupleAs<T(Args...)> { ... };
Compiler errors
However, if I try to do something similar with the ArrayAs version:
template <typename T> struct ArrayAs;
template <typename T, typename U, unsigned N>
struct ArrayAs<T(U,N)> { ... };
I get the following errors in clang (3.6) when trying to instantiate it (ArrayAs< B(double,2)> test;):
typeAs.cpp:14:22: error: unknown type name 'N'
struct ArrayAs<T(U,N)>{
^
typeAs.cpp:14:10: warning: class template partial specialization contains a template parameter that cannot be deduced; this partial specialization will never be used
struct ArrayAs<T(U,N)>{
^~~~~~~~~~~~~~~
typeAs.cpp:13:45: note: non-deducible template parameter 'N'
template<typename T, typename U, unsigned N>
^
The gcc error diagnostic is a little different, but I won't post it here.
I admit that my templating skills should be better than they are, and I also concede that an analogous std::function<B(double,2)> declaration clearly is nonsense. But can someone tell me why the particular syntax I'm trying to achieve is not allowed? I looked through the C++14 standard and had trouble finding the relevant portion, and I'm having trouble interpreting the clang diagnostic message.
When you specialize TupleAs:
template <typename T, typename ... Args>
struct TupleAs<T(Args...)>
You are basically overloading the notation for a function. You are specializing on a function that takes Args... and returns a T. That is a type. You may not be using that function as a function, or really ever think about it as being a type, but that is what it is.
On the other hand, here:
template <typename T, typename U, unsigned N>
struct ArrayAs<T(U,N)> { ... };
There is no such thing as a function that takes N. It could take unsigned, but it can't take a value. There is just no such reasonable thing. From your example, B(double, 2) simply does not make sense. At best, you could write something that would allow:
template <unsigned N> using size_ = std::integral_constant<size_t, N>;
ArrayAs< B(double,size_<2>) >
Or even:
ArrayAs< B(std::array<double, 2>) >
since now we're back to using types everywhere. Whether you prefer that or not is personal preference.
The key here is that types are first-class citizens when it comes to all things template metaprogramming, and values should be avoided where possible.
template <typename T> struct ArrayAs;
template <typename T, typename U, std::size_t N>
struct ArrayAs<T(std::array<U,N>)> { ... };
works, as would:
template<class T>
struct to_array;
template<class T, size_t N>
struct to_array< T[N] > { using type = std::array<T, N>; };
template<class T>
using arr = typename to_array<T>::type;
then:
ArrayAs< Bob( arr<int[3]> ) > some_var;
live example.
Sadly, directly using ArrayAs< Bob( int[3] ) > doesn't work due to how arrays in function types decay to pointers.

VS2013 C++ C1001 error with std::tuple_cat

I recently migrated a C++ code from VS2012 to VS2013. The code was compiling under VS2012 but VS2013 throws a C1001 Internal compiler error.
Specifically, the error points to the tuple.h file from the std library:
template<class... _Types1,
class _Kx_arg,
size_t... _Ix,
size_t _Ix_next,
class... _Types2,
class... _Rest>
struct _Tuple_cat2<tuple<_Types1...>, _Kx_arg, _Arg_idx<_Ix...>, _Ix_next,
tuple<_Types2...>, _Rest...>
: _Tuple_cat2<
tuple<_Types1..., _Types2...>,
typename _Cat_arg_idx<_Kx_arg,
typename _Make_arg_idx<_Types2...>::type>::type,
_Arg_idx<_Ix..., _Repeat_for<_Ix_next, _Types2>::value...>,
_Ix_next + 1,
_Rest...>
{ // determine tuple_cat's return type and _Kx/_Ix indices
};
My code calls the std::tuple_cat method in order to retrieve the type of concatenated tuples (note the partial specializations with the void type):
template <typename TupleA, typename TupleB>
struct tuple_concatenator//yields the type of two concatenated tuples.
{
typedef decltype(std::tuple_cat(std::declval<TupleA>(),
std::declval<TupleB>())) type;
};
template <typename TupleA>
struct tuple_concatenator<TupleA, void>//yields the type of TupleA.
{
typedef TupleA type;
};
template <typename TupleB>
struct tuple_concatenator<void, TupleB>//yields the type of TupleB.
{
typedef TupleB type;
};
template <>
struct tuple_concatenator<void, void>
{
typedef void type;
};
How would you configure VS2013 or rewrite the aforementioned code to avoid the C1001 error?
Thank you in advance for your help.
An ICE is always a compiler bug. File a bug with Microsoft (also, try and see if you can reproduce it on http://webcompiler.cloudapp.net/, which runs VS2015 RC).
std::tuple_cat is tricky to implement because it needs to be able to efficiently concatenate an arbitrary number of tuples - both the type and the value. But if you just need to concatenate two std::tuple<...> types, you don't need the complicated tuple_cat machinery; it's straightforward:
template <typename TupleA, typename TupleB>
struct tuple_concatenator;
template <class... Ts, class... Us>
struct tuple_concatenator<std::tuple<Ts...>, std::tuple<Us...>>
{
typedef std::tuple<Ts..., Us...> type;
};
This should work just fine with your existing partial and full specializations for void.

Template deduction fails for std:map as template parameter

I'm implementing a simple LRU cache in C++11. I pretty much have it covered but there's just one minor problem. Let's say I have the following template class definition:
#ifndef _LRU_STL_H_
#define _LRU_STL_H_
#include <functional>
#include <cassert>
#include <list>
template <typename KeyType, typename ValueType, template<typename...> class Map>
class LRU {
public:
typedef Map<KeyType, std::pair<ValueType, typename std::list<KeyType>::iterator>> KeyToValueType;
LRU(const std::function<ValueType(const KeyType&)> &Function, size_t Capacity)
: _Function(Function), _Capacity(Capacity) {
assert(_Capacity != 0);
}
...
private:
...
std::function<ValueType(const KeyType&)> _Function;
const size_t _Capacity;
KeyToValueType _KeyToValue;
};
#endif
At KeyToValue type I get the following compilation error with MSVC2013: Error 1 error C2976: 'std::map' : too few template arguments c:\x\visual studio 2013\projects\caching\lru_stl\lru_stl.h 17 1 LRU_STL
The 17th line is:
typedef Map<KeyType, std::pair<ValueType, typename std::list<KeyType>::iterator>> KeyToValueType;
Seems like the template deduction fails. It may be a very simple problem but I just couldn't find it yet. For completeness here's an example:
std::function<std::string(std::string)> functionToCache = [](std::string & str) {
std::string reverse;
reverse.reserve(str.size());
std::copy(str.begin(), str.end(), reverse);
return reverse;
};
LRU<std::string, std::string, std::map> LRU(functionToCache, 5);
std::string Hello_World = LRU("Hello World");
assert(Hello_World == "dlroW olleH");
The error is already provided. Done mentioned fixes. Still the same error occurs: std::map too few template arguments.
Just for completeness if I remove everything and create a TEST class:
template <typename A, typename B, template <typename ...> class Map>
class TEST {
typename Map<A, std::pair<B, typename std::list<A>::iterator>> CMAP;
public:
TEST(void) { }
};
Trying to instantiate the class results in the exact same error message.
#Update:
VC++ Compiler seems to be unable to process default template parameters in this particular scenario. To solve the issue I had to add all four template parameters to the typedef and so the definition became like:
template <typename K, typename V, template <typename...> class Map>
class Test {
typedef Map<K, std::pair<V, typename std::list<K>::iterator>, std::less<K>, std::allocator<std::pair<const K, typename std::list<K>::iterator>>> MapType;
};
That would be all to this issue. Thanks for all who tried to help and for that professional gentleman with: 'I don't have even the slightest idea about this question, Let's DOWNVOTE it!!!'. You really are amazing! Wish you the best man....
Your missed two points.
First, template template parameter should be like this:
template < parameter-list > class name
So your
template<typename...> Map
should be
template<typename...> class Map
Second, you should use typename with dependent names. In your code, std::list<Key>::iterator is a dependent name (depending on Key). So, you should use typename std::list<Key>::iterator instead.
Here's my corrected test code.
#include <list>
#include <map>
template <typename Key, typename Value,
template <typename...> class Map>
class Test
{
public:
typedef Map<
Key,
std::pair<Value, typename std::list<Key>::iterator>
> KeyToValueType;
};
int main()
{
Test<int, char, std::map>::KeyToValueType asdf;
}
It worked both in g++ 4.9.1 and in clang++ 3.5.
It seems to be due to VC++'s foolishness. It may work if you give the full template parameter to std::map, including comparer and allocator, since VC++ seems not to be able to process default template parameter in this case.