Different member function definition according to compile-time condition - c++

As per this answer, I've been using
template <typename T,
typename = typename enable_if<bool_verfier<T>()>::type> >
classMember(const T& arg);
As the function signature for several class members, where bool_verifier<T>() is a templated function that asserts that a particular class T fulfills certain requirements, with return type constexpr bool. This ensures a particular overload of classMember(const T& arg) is only used for particular argument types, but it is not possible to do this when there are multiple overloads with the same prototype/argument signature, because the compiler won't allow it:
// ...
template <typename T, typename = typename enable_if<bool_verfier<T>()>::type> >
classMember(const T& arg);
template <typename T, typename = typename enable_if<!(bool_verfier<T>())>::type>>
classMember(const T& arg);
// ...
which causes the following compilation error:
‘template<class T, class> void myClass::classMember<T>(const T&)’
cannot be overloaded with
‘template<class T, class> void std::myClass<T>::classMember(const T&)’
If I need classMember to have different definitions according to whether or not bool_verifier<T>() returns true, what would be the correct syntax/member declaration? Alternatively, is there a way to call bool_verifier<T> from an #if precompiler conditional statement?

Alternatively, is there a way to call bool_verifier<T> from an #if precompiler conditional statement?
Nope. The preprocessor runs before anything else, and doesn't have knowledge of C++ at all.
You probably need to disambiguate between the two overloads with an extra template parameter (or by changing where enable_if appears), as default template parameter values are not part of the signature. The following works for me:
struct foo
{
template <typename T, typename = std::enable_if_t<bool_verifier<T>{}>>
void a();
template <typename T, typename = std::enable_if_t<!bool_verifier<T>{}>, typename = void>
void a();
};
live godbolt.org link

Related

disable template member function if return type is an array

https://www.godbolt.org/z/_4aqsF:
template <typename T> struct Container
{
template <typename TPred> T find_if(TPred pred); // the culprit
};
template <typename T> Container<T> MakeContainer(T const &)
{
return Container<T>();
}
int main()
{
auto x = MakeContainer("Hello!");
}
gcc, clang and msvc apparently agree that this cannot compile because find_if would return an array.
(I would have assumed that the member template isn't instantiated since it doesn't get used - apparently, this simplistic view is wrong.)
Why does SFINAE not apply here?
Is there a way to exclude the member template for types where T is not a returnable type?
SFINAE is not in play because the members of the types produced in your MakeContainer return point are not examined during SFINAE of MakeContainer overloads.
SFINAE happens only in an immediate context. Bodies of types and functions are not in-scope and do not cause a subsitution failure.
template <typename T=char[7]> Container<char[7]> MakeContainer(char const (&)[7])
this signature is fine.
Once selected, the Container<char[7]> is instantiated and its methods parsed.
template <typename TPred> char[7] find_if(TPred pred); // the culprit
there is no TPred that could cause this find_if to be a valid method, so your program is ill formed no diagnostic required.
The correct fix is:
template <typename T> struct Container
{
template <typename TPred> T find_if(TPred pred); // the culprit
};
template <class T, std::size_t N> struct Container<T[N]>:
Container<std::array<T,N>>
{
using Container<std::array<T,N>>::Container;
};
of course, Container<std::array<T,N>> itself needs a very special find_if and probably constructors. But at least it doesn't break immediately.
SFINAE removes from overload sets the overloads which would be illegal during template argument deduction.
Here, the overload set contains only one candidate: MakeContainer<const char (&)[7]>. Template argument deduction ends here. No ambiguity. Everything's fine.
Then, the type Container<const char (&)[7]> is instantiated. And it produces a templated function (Container<const char (&)[7]>::find_if) whose signatures are illegal (all of them, since T is deduced in the context of find_if). SFINAE is not at play.
Now, you can add some SFINAE to your container's find_if function by making its return type depend on its template argument. For that, see max66's answer for that.
To use SFINAE on fidn_if you need to use dependent parameters of the function itself, here's a version that SFINAE on non-returnable types:
template <typename TPred, class U = T, typename std::enable_if<
std::is_same<T, U>::value
&& !std::is_abstract<U>::value
&& !std::is_function<U>::value
&& !std::is_array<U>::value
, bool>::type = true>
U find_if(TPred pred);
Try with
template <typename TPred, typename U = T>
U find_if (TPred pred); // the culprit
SFINAE, over methods, doesn't works with templates parameters of the class. Works over templates of the method itself. So you have to make the SFINAE substitution dependant from a template parameter of the method itself.
So not T but U.
If you fear that someone can "hijack" your function explicating the template types as follows
auto x = MakeContainer("Hello!");
x.find_if<int, int>(1);
you can impose that U and T are the same type
template <typename TPred, typename U = T>
typename std::enable_if<std::is_same<U, T>::value, U>::type
find_if (TPred pred) // the culprit

Template aliases conflicting types. g++ compiles successfully while clang fails

I encountered a very strange compiler error. For some reason the posted code does compile properly with g++ (7.3.0) while clang (7.0.0) fails:
../TemplateAlias/main.cpp:64:9: error: no matching function for call to 'freeFunc'
freeFunc(new Func, dummyField);
^~~~~~~~
../TemplateAlias/main.cpp:73:12: note: in instantiation of member function 'Helper<Traits<double, ConcreteData, ConcreteField> >::func' requested here
helper.func();
^
../TemplateAlias/main.cpp:21:13: note: candidate template ignored: deduced conflicting templates for parameter '' ('FieldData' vs. 'ConcreteData')
static void freeFunc(SomeFunc<T, FieldData>* func,
^
Both compiler options were set to -std=c++14
template<typename T>
struct ConcreteData
{
T data;
};
template<typename T, template<typename U> class FieldData>
struct ConcreteField
{
FieldData<T> someMember;
};
template<typename T, template<typename U> class FieldData>
struct SomeFunc
{
};
template<typename T, template<typename U> class FieldData>
static void freeFunc(SomeFunc<T, FieldData>* func,
ConcreteField<T, FieldData>& field)
{
// apply the func on data
(void)field; // silence compiler warning
delete func;
}
template<
typename ScalarType,
template<typename U> class FieldDataType,
template<typename U, template <typename X> class Data> class FieldType
>
struct Traits
{
using Scalar = ScalarType;
template<typename T>
using FieldData = FieldDataType<T>;
using Field = FieldType<Scalar, FieldDataType>; // fails with clang only
// using Field = FieldType<Scalar, FieldData>; // using this line helps clang
};
template<typename Traits>
struct Helper
{
// alias all types given by trait for easier access
using Scalar = typename Traits::Scalar;
using Field = typename Traits::Field;
template<typename U>
using DataAlias = typename Traits::template FieldData<U>;
void func()
{
// using Func = SomeFunc<Scalar, DataAlias>; // this line is intended, but fails with both GCC and clang
using Func = SomeFunc<Scalar, Traits::template FieldData>; // compiles only with GCC, fails with clang
Field dummyField;
freeFunc(new Func, dummyField);
}
};
int main()
{
using ConcreteTraits = Traits<double, ConcreteData, ConcreteField>;
Helper<ConcreteTraits> helper;
helper.func();
return 0;
}
According to cppreference.com:
A type alias declaration introduces a name which can be used as a
synonym for the type denoted by type-id. It does not introduce a new
type and it cannot change the meaning of an existing type name. There
is no difference between a type alias declaration and typedef
declaration. This declaration may appear in block scope, class scope,
or namespace scope.
and
Alias templates are never deduced by template argument deduction when
deducing a template template parameter.
In my understanding both types (ConcreteData and FieldData) should be equivalent. Why is clang failing in this condition and why do both compiler fail when using the "second stage" alias? Which compiler is right according to the C++ standard? Is it a compiler bug or a subtle ambiguous interpretation of the C++14 standard?
Borrowing the minimal example of #Oktalist.
template <typename>
class T {};
template <typename _>
using U = T<_>;
template <template <typename> class X>
void f(A<X>, A<X>) {}
if you replace f by:
template <template <typename> class X, template <typename> class Y>
void f(A<X>, A<Y>) {}
the code no longer fail to compile. You can see that the problem is about equivalence of template parameters X and Y, they are deduced to different types.
The equivalence of types produced by alias template are only considered when referring to specialization of the alias, as is specified on [temp.alias]/2:
When a template-id refers to the specialization of an alias template, it is equivalent to the associated type obtained by substitution of its template-arguments for the template-parameters in the type-id of the alias template
Using this rule and the rules for equivalence [temp.type]/1:
T<int> and U<int> are equivalent, so are X<T<int>> and Z<U<int>>, but this rule doesn't extend to the alias template U being equivalent to the class template T (by themselves, they aren't specializations).
This is the same scenario for the alias FieldData and the class template ConcreteData.
There are in fact two defect report, CWG-1286 and CWG-1244 that propose the equivalence extension for alias templates.

When specializing a class, how can I take a different number of template parameters?

I just asked this question: Can I get the Owning Object of a Member Function Template Parameter? and Yakk - Adam Nevraumont's answer had the code:
template<class T>
struct get_memfun_class;
template<class R, class T, class...Args>
struct get_memfun_class<R(T::*)(Args...)> {
using type=T;
};
These is clearly an initial declaration and then a specialization of struct get_memfun_class. But I find myself uncertain: Can specializations have a different number of template parameters?
For example, is something like this legal?
template<typename T>
void foo(const T&);
template<typename K, typename V>
void foo<pair<K, V>>(const pair<K, V>&);
Are there no requirements that specializations must take the same number of parameters?
You seem to confuse the template parameters of the explicit specialization and the template arguments you use to specialize the template.
template<class T> // one argument
struct get_memfun_class; // get_memfun_class takes one template (type) argument
template<class R, class T, class...Args>
struct get_memfun_class<R(T::*)(Args...)> {
// ^^^^^^^^^^^^^^^^
// one type argument
using type=T;
}; // explicit specialization takes one template argument
Yes, there are three template parameters for the explicit specialization, but that doesn't mean that the explicit specialization takes three arguments. They are there to be deduced. You can form a single type using multiple type parameters, which is what is happening there. Also consider that you can fully specialize a template:
template <>
struct get_memfun_class<void>;
// ^^^^
// one type argument
Here it's the same thing. Yes, the explicit specialization takes no parameters, but that just means that there is none to be deduced and indeed you are explicitly writing a template parameter (void) and so the amount of template arguments of the specialization match those of the primary template.
Your example is invalid because you cannot partially specialize functions.
Are there no requirements that specializations must take the same number of parameters?
There is; and is satisfied in your example.
When you write
template<class T>
struct get_memfun_class;
you say that get_mumfun_class is a template struct with a single template typename argument; and when you write
template<class R, class T, class...Args>
struct get_memfun_class<R(T::*)(Args...)> {
using type=T;
};
you define a specialization that receive a single template typename argument in the form R(T::*)(Args...).
From the single type R(T::*)(Args...), you can deduce more that one template paramenters (R, T and the variadic Args..., in this example) but the type R(T::*)(Args...) (a method of a class that receive a variadic list of arguments) remain one.
For example, is something like this legal?
template<typename T>
void foo(const T&);
template<typename K, typename V>
void foo<pair<K, V>>(const pair<K, V>&);
No, but (as written in comments) the second one isn't a class/struct partial specialization (where std::pair<K, V> remain a single type), that is legal; it's a template function partial specialization that is forbidden.
But you can full specialize a template function; so it's legal (by example)
template<>
void foo<std::pair<long, std::string>(const std::pair<long, std::string>&);
as is legal the full specialization for get_memfun_class (to make another example)
template<>
struct get_memfun_class<std::pair<long, std::string>> {
using type=long long;
};

Parameter pack must be at the end of the parameter list... When and why?

I don't get the reason for which a parameter pack must be at the end of the parameter list if the latter is bound to a class, while the constraint is relaxed if the parameter list is part of a member method declaration.
In other terms, this one compiles:
class C {
template<typename T, typename... Args, typename S>
void fn() { }
};
The following one does not:
template<typename T, typename... Args, typename S>
class C { };
Why is the first case considered right and the second one is not?
I mean, if it's legal syntax, shouldn't it be in both the cases?
To be clear, the real problem is that I was defining a class similar to the following one:
template<typename T, typename... Args, typename Allocator>
class C { };
Having the allocator type as the last type would be appreciated, but I can work around it somehow (anyway, if you have a suggestion it's appreciated, maybe yours are far more elegant than mine!!).
That said, I got the error:
parameter pack 'Args' must be at the end of the template parameter list
So, I was just curious to fully understand why it's accepted in some cases, but it is not in some others.
Here is a similar question, but it simply explains how to solve the problem and that was quite clear to me.
It is valid for function templates but only when argument deduction can help the compiler resolve the template parameters, as it stands your function template example is virtually useless because
template<typename T, typename... Args, typename S> void fn() { }
int main() { fn<int, int, int>(); }
test.cpp: In function 'int main()':
test.cpp:2:32: error: no matching function for call to 'fn()'
int main() { fn<int, int, int>(); }
^
test.cpp:1:57: note: candidate: template<class T, class ... Args, class S> void fn()
template<typename T, typename... Args, typename S> void fn() { }
^
test.cpp:1:57: note: template argument deduction/substitution failed:
test.cpp:2:32: note: couldn't deduce template parameter 'S'
int main() { fn<int, int, int>(); }
the compiler has no way of determining which template parameters belong to the parameter pack, and which to S. In fact as #T.C. points out it should actually be a syntax error because a function template defined in this manner cannot ever be instantiated.
A more useful function template would be something like
template<typename T, typename... Args, typename S> void fn(S s) { }
as now the compiler is able to unambiguously match the function parameter s with the template type S, with the side effect that S will always be deduced - all explicit template parameters after the first will belong to Args.
None of this works for (primary) class templates, parameters aren't deduced and it's expressly forbidden:
From draft n4567
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4567.pdf
[temp.param] / 11
[...]If a template-parameter of a primary class template or alias
template is a template parameter pack, it shall be the last
template-parameter.[...]
(if they were deduced it would be ambiguous as in the function template example).
The first one is not right. The compiler is just buggy and failed to diagnose it. [temp.param]/11:
A template parameter pack of a function template shall not be followed
by another template parameter unless that template parameter can be
deduced from the parameter-type-list of the function template or has a
default argument (14.8.2).
If the function type T(Args...) is meaningful to the end-user, one way to fix this would be to use a partial specialization instead:
template<class F, class Alloc> class C; //undefined
template<class T, class... Args, class Alloc>
class C<T(Args...), Alloc> {
// implementation
};
Depending on the actual requirements, type-erasing the allocator might also be worth considering.

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&);