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.
Related
Consider the following code:
template <typename T>
struct X
{
void foo(){}
};
class Y { };
template <typename T, typename... U>
class Example {
protected:
template <template<typename> typename example_t>
using alias = X<example_t<T>>;
};
template <typename T>
struct ExampleA : public Example<T, Y>
{
using MyAlias = ExampleA::alias<ExampleA>;
};
In C++14 I could do the following and have it work as expected:
ExampleA<int>::MyAlias test;
test.foo();
A recent upgrade to C++17 now gives a warning 'ExampleA<T>::alias': dependent name is not a type as well as syntax error: identifier 'alias'.
Normally when you get something involving a dependent name it means you need to add the typename keyword as in the following example (iterator is dependent upon std::vector<T>):
template<typename T>
void bar() {
/* typename */ std::vector<T>::iterator it;
}
However, I don't believe that to be the case here. Also, using using MyAlias = Example<T, Y>::alias<ExampleA>; results in the same error.
Did a change in C++17 invalidate this code, or is this a compiler bug? What can I do to fix this in C++17?
MSVC ignored the abscence of desambiguators due to an implementation detail of their compiler.
With new advances and reengineering of their compiler, they now implement two phase name lookup as they should. However, with that implemented, it's really hard in some cases to ignore absence of desambiguators.
They became even more strict with the /permissive- flag, which is an attempt to disable most of their extensions due to their previous lack of two phase name lookup.
Your code snippet look like this with desambiguators:
template <typename T>
struct ExampleA : public Example<T, Y>
{
using MyAlias = typename ExampleA::template alias<ExampleA>;
};
See it as an opportunity to upgrade the compilance and portability of your code.
However with C++20, many cases where desambigurator were needed are now optional:
template<typename T>
auto wrapper() -> T::type; // no typename!
I'm currently wrestling with Visual Studio 2017 (compiling using /std:c++latest if that's any help).
The code in question simply selects a struct specialization based on the result of some templated constexpr function. GCC and clang have no trouble compiling it.
Here's my MCVE:
#include <type_traits>
struct A {
enum {
test_trait = true
};
};
template<typename T>
constexpr int choose() {
return T::test_trait;
}
template<typename T, typename Enable=void>
struct Chosen;
template<typename T>
struct Chosen<T, std::enable_if_t<choose<T>() == 1>> {};
void foo() {
// This works
constexpr int chosen = choose<A>();
static_assert(chosen == 1, "");
// This resolves to the undefined struct.
using Chosen_t = Chosen<A>;
Chosen_t x;
(void)x;
}
choose() is actually a fair bit more complex in my codebase, but the static_assert still compiles, and checks fine.
I kinda assumed that if the static_assert compiles, there is no reason for the enable_if to not be able to do its magic. Am I wrong? I guess "maybe" T is not technically a dependant type of the enable_if... But if that was the case, I'd expect GCC and clang to slap my wrist.
I can get around this by wrapping the result of choose() in a std::integral_constant, like so:
template<typename T>
struct Chooser : public std::integral_constant<int, choose<T>()> {};
template<typename T>
struct Chosen<T, std::enable_if_t<Chooser<T>::value>> {};
But I'd really rather not have to jump through that hoop.
Should template resolution be able to resolve this the way I expect? I'm worried that the code is actually wrong, and GCC and clang are just being lenient on me.
Code still appears to be broken in MSVC 19.00.23506. However, it appears to work with just one more level of indirection (perhaps a better workaround):
template<typename T, bool>
struct ChosenImpl;
template<typename T>
struct ChosenImpl<T, true> {};
template<typename T>
using Chosen = ChosenImpl<T, choose<T>()>;
Demo
A benefit to this is that we are hiding the second template argument from the caller, which they don't care about anyway.
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.
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).
I've found a weird issue on g++ 4.4 and 4.5. I've asked about this because i thought that i was making some silly error in the code. The original post is here but for post completitude i'll repost the problematic code in question here:
$ cat templatetemplate.cc
template <int i>
struct LabelTypeMap { typedef int type_t; };
template <bool>
struct Hold { typedef int type; };
template<typename Holder, template<typename Holder::type> class typeMap>
struct Whatever { };
template <bool Enable>
struct Now { typedef Whatever<Hold<ENABLE>, LabelTypeMap> concrete_t; };
Now<true>::concrete_t obj;
$ g++ -DENABLE=Enable -c templatetemplate.cc
templatetemplate.cc:11: error: type/value mismatch at argument 2 in template parameter list for ‘template<class Holder, template<typename Holder::type <anonymous> > class typeMap> struct Whatever’
templatetemplate.cc:11: error: expected a template of type ↵
‘template<typename Holder::type <anonymous> > class typeMap’, got ↵
‘template<int i> struct LabelTypeMap’
marcelo#macbookpro-1:~/play$
$ g++ -DENABLE=true -c templatetemplate.cc
(no error)
It does not seem to be really a programmer error, althought it might be possible i'm missing some obscure rule of the template template parameter resolution. However i tried posting the bug to ubuntu tracker (hopefully they'll dismiss it or otherwise send the bug upstream)
So, just for the sake of checking if this is really a bug, i got myself a copy of the 2003 standard, and i have read section 14.3.3 a couple of times now, and still i feel i miss the slightest clue if passing a template template parameter with a parameter as in the sample code is allowed or disallowed. I'm not even sure this part of the document is mentioning anything about this
Here goes my question: do you know where this is specified?
EDIT: it is pretty interesting that this question has gone unanswered for over a week now: It leads me to believe that ISO c++ standard does not specify if we can use a previous template parameter to specify types of subsequent template parameters (at least in the stated form) and that is basically left for implementers to decide
2nd EDIT (10/01/2011): People, there is probably something about this that we are all missing (or else lots of highly skilled compiler designers are wrong):
I tried this with intel c++ compiler XE 12.0 and i got this:
$icpc ttemplatetemplate.cc -o ./x2test
templatetemplate.cc(12): error: class template "LabelTypeMap" is not compatible with template template parameter "typeMap"
struct Now { typedef Whatever<Hold<Enable>, LabelTypeMap> concrete_t; };
^
compilation aborted for templatetemplate.cc (code 2)
$ icpc --version
icpc (ICC) 12.0.0 20101116
Copyright (C) 1985-2010 Intel Corporation. All rights reserved.
I cannot find anything in the standard which forbids it, although I tried this simple code (which seems to me is a simplification of your problem) in Comeau :
template<int>
class A {};
template<class T, template<T> class U>
class B {};
B<int, A> b;
And it produces the following error :
"ComeauTest.c", line 4: error: a
parameter of a template template
parameter cannot
depend on the type of another template parameter
I wish I could find which part of the standard actually forbids it...
BTW, here's the solution I found to the underlying problem:
template <int i>
struct LabelTypeMap { typedef int type_t; };
template <bool>
struct Hold { typedef int type; };
template<typename Holder>
struct Whatever {
typedef typename Holder::type HT;
template <template <HT> class typeMap>
struct Whatever2 { };
};
template <bool Enable>
struct Now { typedef typename Whatever<Hold<Enable> >::Whatever2<LabelTypeMap> concrete_t; };
Using a nested template, I can introduce a typename via a typedef.
I'm suspecting this weird construct: template<typename Holder::type> class typeMap
What's that supposed to be? The typename Holder::type bit is weird; that's supposed to be a dummy name (identifier). Holder::type isn't even an identifier.
Isn't this how it is supposed to be?
This is because LabelTypeMap is itself a template (template template parameter) and hence requires a type to be specified.
template <bool Enable>
struct Now { typedef Whatever<Hold<ENABLE>, LabelTypeMap<ENABLE> > concrete_t; };