When I try to use the following meta function to retrieve the first type of a tuple, the code can be compiled with GCC but not with Clang. I have two questions regarding the little snippet.
Is this legal C++ code? And why? Or why not?
Is there a workaround (or correct alternative) which works for both compilers?
#include <tuple>
template<typename>
struct first_type;
template<template<typename, typename...> typename T, typename T1, typename... Ts>
struct first_type<T<T1, Ts...>>
{ using type = T1; };
template<typename T>
using first_type_t = typename first_type<T>::type;
using tuple_type1 = first_type_t<std::tuple<int, int, double>>;
Live example
As requested, the error message generated by Clang:
<source>:12:1: error: implicit instantiation of undefined template
'first_type<std::tuple<int, int, double>>'
using first_type_t = typename first_type<T>::type;
^
<source>:14:21: note: in instantiation of template type alias
'first_type_t' requested here
using tuple_type1 = first_type_t<std::tuple<int, int, double>>;
^
<source>:4:8: note: template is declared here
struct first_type;
^
In conclusion:
As answered by IWonderWhatThisAPIDoes; to circumvent the compiler discrepancy completely, simply drop the requirement of the templated template argument to have at least a single type.
As pointed out by Nathan Oliver; if you need the first type of a tuple (or really any type given an index), simply use the std::tuple_element meta function instead.
As pointed out by HolyBlackCat; it seems the ruling that Clang enforces regarding templated template arguments, is stricter than technically required by the standard. This behavior can be disabled by passing the -frelaxed-template-template-args compiler flag.
Temporarily changing your struct declaration to (thus getting rid of the incomplete type):
template<typename>
struct first_type {};
Changes the error you get to:
no type named 'type' in 'first_type<std::tuple<int, int, double>>'
This gives us valuable information: the compiler chose the generic version of the template, implying that Clang does not consider tuple to be a valid template<typename,typename...>class to instantiate first_type with (which is true - template<typename,typename...>class takes one or more parameters, while a tuple can also be empty). As pointed out in the comments, this does not matter to the standard itself, but Clang rejects it on purpose.
A tuple is a template<typename...>class, so...
template<template<typename...> typename T, typename T1, typename... Ts>
struct first_type<T<T1, Ts...>> {
using type = T1;
};
Related
The following code compiles successfully in g++ 7.2.0 (compilation flags are -std=c++14 -Wall -Wextra -Werror -pedantic-errors), but it fails to compile in clang++ 5.0.0 (with the same flags, -std=c++14 -Wall -Wextra -Werror -pedantic-errors) and vc++ 15.4 (compilation flags are /EHsc /Za /std:c++14 /permissive-):
template <template <typename...> class Functor, typename... FixedArguments>
struct apply
{
template <typename... FreeArguments>
using type = Functor<FixedArguments..., FreeArguments...>;
};
template <typename, typename>
struct Bar{};
template <template <typename...> class>
struct Foo{};
int main()
{
(void)Foo<apply<Bar, int, char>::type>{};
}
Which compiler behavior is standard compliant? How such template apply may be changed to be compiled on clang++, too?
clang++ error messages:
5 : <source>:5:15: error: too many template arguments for class template 'Bar'
using type = Functor<FixedArguments..., FreeArguments...>;
^ ~~~~~~~~~~~~~~~~~
16 : <source>:16:15: note: in instantiation of template class 'apply<Bar, int, char>' requested here
(void)Foo<apply<Bar, int, char>::type>{};
^
9 : <source>:9:8: note: template is declared here
struct Bar{};
vc++ error messages:
5 : <source>(5): error C2977: 'Bar': too many template arguments
9 : <source>(9): note: see declaration of 'Bar'
16 : <source>(16): note: see reference to class template instantiation 'apply<Bar,int,char>' being compiled
note: after looking at this, this answer would be correct if Bar were an alias template rather than a class template. The workaround works but for other reasons. See Constructors answer for the correct answer of the OP.
This problem is known as the 'alias flaw' and we had a lot of challenges with it in the implementation of kvasir::mpl. The problem is that Bar takes exactly two parameters but sizeof...(FixedArguments)+sizeof...(FreeArguments) could add up to something besides 2.
The compiler can either try and track the potential arity through all the alias calls and only issue errors when the user actually passes something besides 2 or it can "eagerly" give an error simply by proving that an error could occur.
The work around I have found effective in dealing with this is to make the alias call dependant on the size of the input https://godbolt.org/g/PT4uaE
template<bool>
struct depends{
template<template<typename...> class F, typename...Ts>
using f = F<Ts...>;
};
template<>
struct depends<false>{
template<template<typename...> class F, typename...Ts>
using f = void;
};
template <template <typename...> class Functor, typename... FixedArguments>
struct apply
{
template <typename... FreeArguments>
using type = typename depends<(sizeof...(FixedArguments)+sizeof...(FreeArguments) == 2)>::template f<Functor, FixedArguments..., FreeArguments...>;
};
template <typename, typename>
struct Bar{};
template <template <typename...> class>
struct Foo{};
int main()
{
(void)Foo<apply<Bar, int, char>::type>{};
}
It should be noted that constraining to exactly two is not needed on all compiler I have tested, one could just as easilty constrain to be sizeof...(FixedArguments)+sizeof...(FreeArguments) != 100000 and the compiler will still take it only issuing an error if things actually don'T work out on a concrete call.
I would actually like to improve my mental model of how this works internally in order to come up with faster work arounds, in kvasir::mpl we are currently experimenting with tracking the arity manually behind the scenes in order to eliminate the dependant calls which do slow things down a little.
As #T.C. noted in the comments to the question such code is ill-formed (no diagnostic required).
C++14 standard, section "Name resolution" [temp.res], paragraph 8:
If every valid specialization of a variadic template requires an empty
template parameter pack, the template is ill-formed, no diagnostic
required.
Latest drafts of the C++ standard, section "Name resolution" [temp.res], paragraph 8.3:
...The program is ill-formed, no diagnostic required, if:
...
every valid specialization of a variadic template requires an empty template parameter pack...
Additional information: Core Issue 2067.
In accordance with the standard requirements such simple workaround can be written:
template <template <typename...> class Functor, typename... Arguments>
struct invoke
{
using type = Functor<Arguments...>;
};
template <template <typename...> class Functor, typename... FixedArguments>
struct apply
{
template <typename... FreeArguments>
using type = typename invoke<Functor, FixedArguments..., FreeArguments...>::type;
};
Live demo
Update: As #odinthenerd noted in the comments this workaround uses an additional type which leads to a slower compilation of the program.
How can I check at compile-time whether or not an arbitrary type can be used with std::pointer_traits? I had hoped a simple SFINAE solution might work:
template <typename T, typename = void>
struct pointer_traits_ready : std::false_type {};
template <typename T>
struct pointer_traits_ready<
T,
std::void_t<typename std::pointer_traits<T>::element_type>
> : std::true_type {};
static_assert(!pointer_traits_ready<int>::value,"");
...but this invokes a static assert from within the standard library (ptr_traits.h). Obviously std::is_pointer doesn't accommodate smart pointers.
You can't. From [pointer.traits.types]:
using element_type = see below ;
Type: Ptr::element_type if the qualified-id Ptr::element_type is valid and denotes a type (14.8.2); otherwise, T if Ptr is a class template instantiation of the form SomePointer<T, Args>, where Args is
zero or more type arguments; otherwise, the specialization is ill-formed.
In order to be SFINAE-friendly, we would need pointer_traits<Foo> to simply lack a type alias named element_type. The problem is, element_type is specified as being ill-formed - not absent. So you simply cannot use pointer_traits as a detector for whether or not something can be used as a pointer type.
Even if you wrote your own type that was a SFINAE-friendly version of that specification, you wouldn't be able to catch user specializations of pointer_traits for their own type. Sad panda.
In an attempt to use SFINAE, the following code fails to compile:
template<typename ObjectType, typename GroupA, typename GroupB, typename = void>
struct DelegateImpl; // default version
template<typename ObjectType, typename GroupA, typename GroupB>
struct DelegateImpl<ObjectType, GroupA, GroupB, decltype(GroupA::get<ObjectType>())>; // specialization
With GCC:
error: template argument 4 is invalid
With MSVC, a surprisingly more helpful:
error C3553: decltype expects an expression not a type
My aim is to have the compiler pick the specialization if the expression GroupA::get<ObjectType>() is valid.
Question: How do I use decltype with a static template method?
Neither compilers give helpful errors actually. The real issue is you're missing the template keyword before get:
template get<ObjectType>()
See cppreference's page on Dependent Names
An allocator can optionally have nested types like pointer, const_pointer. But one can always use these interface with std::allocator_traits<Allocator>, which would provide a default version of these types if they are absent in Allocator.
How is std::allocator_traits implemented? How can a template choose a default version of nested type when it's absent?
The solution is to refer to the type T::pointer in a context where it does not cause an error if it is not a valid type, instead it causes template argument deduction to fail. The general form of this is known as SFINAE, which stands for "Substitution Failure Is Not An Error". For a explanation of how it works see my SFINAE Functionality Is Not Arcane Esoterica presentation.
There are various techniques, often involving overloaded function templates, but my current favourite uses the void_t idiom to select a partial specialization of a class template:
template<typename T>
using void_t = void;
template<typename T, typename = void>
struct get_pointer
{
using type = typename T::value_type*;
};
template<typename T>
struct get_pointer<T, void_t<typename T::pointer>>
{
using type = typename T::pointer;
};
Now given an allocator type A you can use typename get_pointer<A>::type to refer to A::pointer if that exists, otherwise A::value_type*
The code above works because when A::pointer is a valid type the partial specialization matches and is more specialized than the primary template, and so gets used. When A::pointer is not a valid type the partial specialization is ill-formed, so the primary template is used.
I am trying to get a template alias for T2<B> starting from an instance of C.
template<typename A>
struct T1
{
template<typename B>
struct T2{};
};
template<typename A>
class C
{
T1<A> t;
};
template<typename A>
using U1=decltype(C<A>::t);
template<typename A, typename B>
using U2=typename U1<A>::T2<B>;
I get a compiler failure with gcc 4.8:
gg.cc:18:28: error: expected ‘;’ before ‘<’ token
using U2=typename U1<A>::T2<B>;
I have used the typename keyword in every sensible location, but can't get the U2 definition to compile.
What is the correct syntax here? It would be even better if I could get a definition of U2 without resorting to U1.
You need to use the template disambiguator to tell the compiler to parse T2 as the name of a template (and the subsequent < and > as delimiters of the corresponding template arguments):
template<typename A, typename B>
using U2=typename U1<A>::template T2<B>;
// ^^^^^^^^
Here is a compiling live example.
Here you can find some more information on when you should use the template disambiguator (and the typename disambiguator as well, although you seem to be aware of that one).
When the compiler compiles the following:
template<typename A, typename B>
using U2=typename U1<A>::T2<B>;
After it reads the nested name specifier U1<A>::, it doesn't know which specialization of U1 it is in, because A is unknown. Each U1 specializaiton could be totally different, and depend on what A is. So it has no idea what kind of name T1 is. In particular it doesn't know whether it is a template name or not. (For example U1<int>::T1 could be totally different to what U1<char>::T1 is.)
For this reason you need to explicitly tell the compiler that T1 is going to be a template name by using the template keyword before T1.