decltype and static template method - c++

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

Related

Compiler discrepancy with simple meta function

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;
};

SFINAE `std::void_t` class template specialisation [duplicate]

Are multiple class template specialisations valid, when each is distinct only between patterns involving template parameters in non-deduced contexts?
A common example of std::void_t uses it to define a trait which reveals whether a type has a member typedef called "type". Here, a single specialisation is employed. This could be extended to identify say whether a type has either a member typedef called "type1", or one called "type2". The C++1z code below compiles with GCC, but not Clang. Is it legal?
template <class, class = std::void_t<>>
struct has_members : std::false_type {};
template <class T>
struct has_members<T, std::void_t<typename T::type1>> : std::true_type {};
template <class T>
struct has_members<T, std::void_t<typename T::type2>> : std::true_type {};
There is a rule that partial specializations have to be more specialized than the primary template - both of your specializations follow that rule. But there isn't a rule that states that partial specializations can never be ambiguous. It's more that - if instantiation leads to ambiguous specialization, the program is ill-formed. But that ambiguous instantiation has to happen first!
It appears that clang is suffering from CWG 1558 here and is overly eager about substituting in void for std::void_t.
This is CWG 1980 almost exactly:
In an example like
template<typename T, typename U> using X = T;
template<typename T> X<void, typename T::type> f();
template<typename T> X<void, typename T::other> f();
it appears that the second declaration of f is a redeclaration of the first but distinguishable by SFINAE, i.e., equivalent but not functionally equivalent.
If you use the non-alias implementation of void_t:
template <class... Ts> struct make_void { using type = void; };
template <class... Ts> using void_t = typename make_void<Ts...>::type;
then clang allows the two different specializations. Sure, instantiating has_members on a type that has both type1 and type2 typedefs errors, but that's expected.
I don't believe it's correct, or at least, not if we instantiate has_members with a type that has both type1 and type2 nested, the result would be two specializations that are
has_members<T, void>
which would not be valid. Until the code is instantiated I think it's ok, but clang is rejecting it early. On g++, your fails with this use-case, once instantiated:
struct X
{
using type1 = int;
using type2 = double;
};
int main() {
has_members<X>::value;
}
The error message is doesn't seem to describe the actual problem, but it at least is emitted:
<source>:20:21: error: incomplete type 'has_members<X>' used in nested name specifier
has_members<X>::value;
^~~~~
If you instantiate it with a type that has only type1 or type2 but not both,
then g++ compiles it cleanly. So it's objecting to the fact that the members are both present, causing conflicting instantiations of the template.
To get the disjunction, I think you'd want code like this:
template <class, class = std::void_t<>>
struct has_members : std::bool_constant<false> {};
template <class T>
struct has_members<T, std::enable_if_t<
std::disjunction<has_member_type1<T>, has_member_type2<T>>::value>> :
std::bool_constant<true> {};
This assumes you have traits to determine has_member_type1 and has_member_type2 already written.

Unable to extract value_type from template

I am attempting to extract the value_type from a container using the following code.
//CustomTraits.h
template<typename C, typename = void>
struct has_push_back : std::false_type {};
template<typename C>
struct has_push_back<C, std::void_t< decltype(std::declval<C>())::value_type > > :
std::true_type {};
It is invoked like so
//MessaDecoder.h
template <typename Container,
typename = std::enable_if_t<has_push_back<Container>::value>
>
class MessageDecoder{/*Class Def...*/};
//Server.h
using buffer_t = std::deque<std::tuple<std::string, uint64_t>>;
std::shared_ptr<MessageHelper::MessageDecoder<buffer_t>> _decoder_ptr;
I have tried a significant number of variations in attempting to get this code to compile. I have tested that value_type is valid by extracting this as a template parameter in MessageDecoder.h with Container::value_type and it does compile. However, doing the same and what I have above in CustomTraits.h fails to correctly trigger the specialized template and I am wondering why and how to fix it. Here are some errors I am getting.
Error C3203 'MessageDecoder': unspecialized class template can't be used as a template argument for template parameter '_Ty', expected a real type
Error C2938 'enable_if_t<false,void>' : Failed to specialize alias template
I am using VS2017 C++ 17 the latest update.
Change
decltype(std::declval<C>())::value_type
To
typename C::value_type
declval gives you an rvalue reference, you can't pull members off of reference types (and that dance is unnecessary since you just want the type, which you have: C). And you're missing typename.

Variadic template aliases as template arguments (part 2)

This is a follow-up of another question. It refers to the same problem (I hope) but uses an entirely different example to illustrate it. The reason is that in the previous example only experimental GCC 4.9 failed with a compiler error. In this example, also Clang and GCC 4.8.1 fail in different ways: Clang produces an unexpected result and GCC 4.8.1 reports a different error message.
Answers to the previous question say more or less that the code is valid and the problem lies with the experimental version of GCC. But this result makes me a bit more sceptical. I have been troubled for months with problems that I suspect are related (or the same), and this is the first time I have a small concrete example to illustrate.
So, here is some code. First, some generic code that applies SFINAE to an arbitrary test as specified by a variadic template alias metafunction F:
#include <iostream>
using namespace std;
using _true = integral_constant <bool, true>;
using _false = integral_constant <bool, false>;
template <typename T> using pass = _true;
template <template <typename...> class F>
struct test
{
template <typename... A> static _false _(...);
template <typename... A> static pass <F <A...> > _(int);
};
template <template <typename...> class F, typename... A>
using sfinae = decltype(test <F>::template _<A...>(0));
Second, a specific test, checking if a given class has defined a type named type:
template <typename T> using type_of = typename T::type;
template <typename T> using has_type = sfinae <type_of, T>;
Finally, an example:
struct A { using type = double; };
int main()
{
cout << has_type <int>() << ", ";
cout << has_type <A>() << endl;
}
The expected result would be 0, 1. Clang says 0, 0. GCC 4.8.1 says
tst.cpp: In substitution of ‘template<class T> using type_of = typename T::type [with T = A ...]’:
tst.cpp:15:51: required from ‘struct test<type_of>’
tst.cpp:19:67: required by substitution of ‘template<template<class ...> class F, class ... A> using sfinae = decltype (test:: _<A ...>(0)) [with F = type_of; A = {T}]’
tst.cpp:24:58: required from here
tst.cpp:23:56: error: ‘A ...’ is not a class, struct, or union type
template <typename T> using type_of = typename T::type;
^
and GCC 4.9 says
tst.cpp:19:67: required by substitution of ‘template<template<class ...> class F, class ... A> using sfinae = decltype (test:: _<A ...>(0)) [with F = type_of; A = {T}]’
tst.cpp:24:58: required from here
tst.cpp:15:51: error: pack expansion argument for non-pack parameter ‘T’ of alias template ‘template<class T> using type_of = typename T::type’
template <typename... A> static pass <F <A...> > _(int);
^
(line numbers may vary). So, everything fails, in different ways.
Now, here is a workaround. Metafunction car picks the first type fom a given pack, and then the test is redefined as type_of2, now being variadic:
template <typename... T> struct car_t;
template <typename... T> using car = type_of <car_t <T...> >;
template <typename T, typename... Tn>
struct car_t <T, Tn...> { using type = T; };
template <typename... T> using type_of2 = typename car <T...>::type;
template <typename T> using has_type2 = sfinae <type_of2, T>;
int main()
{
cout << has_type2 <int>() << ", ";
cout << has_type2 <A>() << endl;
}
Now all three compilers say 0, 1 as expected. It is interesting that for any version of GCC we have to remove has_type (even if we don't use it) and leave only has_type2; otherwise we have similar error.
To wrap up: I see the problem with one template expecting a variadic template-parameter of the form
template <typename...> class F
where we actually give as input a non-variadic template alias of the form
template <typename T> using alias = // ... anything including T or not
and finally invoke F as if it was variadic:
F <A...>
Opinions so far say this is valid, but now it seems three compilers disagree. So the question is again: is it valid?
To me it matters because I have dozens of files of existing code based on the assumption that this is valid, and now I need a redesign anyway (since there are practical problems with these compilers) but the exact redesign will depend on the answer.
This does not answer the question whether the code above is valid, but is a quite pretty workaround that I have found by experimenting shortly after asking the question, and I think is useful to share.
All that is needed are the following definitions:
template <template <typename...> class F>
struct temp { };
template <typename... A, template <typename...> class F>
F <A...> subs_fun(temp <F>);
template <template <typename...> class F, typename... A>
using subs = decltype(subs_fun <A...>(temp <F>()));
then, wherever F <A...> would be problematic, replace it with subs <F, A...>. That's it. I cannot explain why, but it has worked in all cases so far.
For instance, in the SFINAE example of the question, just replace line
template <typename... A> static pass <F <A...> > _(int);
by
template <typename... A> static pass <subs <F, A...> > _(int);
This is a change at one point only, all remaining code stays the same. You don't need to redefine or wrap every template metafunction that with be used as F. Here's a live example.
If F <A...> is indeed valid and compilers support it eventually, it is again easy to switch back because changes are minimal.
I find this important because it allows specifying a SFINAE test in just two lines
template <typename T> using type_of = typename T::type;
template <typename T> using has_type = sfinae <type_of, T>;
and is completely generic. Typically, each such test needs at least 10 lines of code and implementations of <type_traits> are full of such code. In some cases such code blocks are defined as macros. With this solution, templates can do the job and macros are not needed.
I think the situation is pretty well standardized; C++11 14.3.3/1 says:
A template-argument for a template template-parameter shall be the name of a class template or an alias template, expressed as id-expression.

Understanding SFINAE

As far as I know, SFINAE means substitution failures do not result in compilation errors, but just remove the prototype from the list of possible overloads.
What I do not understand: why is this SFINAE:
template <bool C, typename T = void> struct enable_if{};
template <typename T> struct enable_if<true, T> { typedef T type; };
But this is not?
template <bool C> struct assert;
template <> struct assert<true>{};
From my understanding, the underlying logic is identical here. This question emerged from the comments to this answer.
In C++98, SFINAE is done with either a return type or a function's dummy argument with default parameter
// SFINAE on return type for functions with fixed arguments (e.g. operator overloading)
template<class T>
typename std::enable_if< std::is_integral<T>::value, void>::type
my_function(T const&);
// SFINAE on dummy argument with default parameter for functions with no return type (e.g. constructors)
template<class T>
void my_function(T const&, std::enable_if< std::is_integral<T>::value, void>::type* = nullptr);
In both cases, substution of T in order to get the nested type type is the essence of SFINAE. In contrast to std::enable_if, your assert template does not have a nested type that can be used in substitution part of SFINAE.
See Jonathan Wakely's excellent ACCU 2013 presentation for more details and also for the C++11 expression SFINAE. Among others (as pointed out by #BartekBanachewicz in the comments) is is now also possible to use SFINAE in function template default arguments
// use C++11 default function arguments, no clutter in function's signature!
template<class T, class dummy = typename std::enable_if< std::is_integral<T>::value, void>::type>
void my_function(T const&);