I'm trying to write a metafunction that checks whether all types passed as a variadic template parameter are distinct. It seems that the most performant way to do this is to inherit from a set of classes and detect, whether there is an error.
The problem is that compilation fails in the following code, while I would expect SFINAE to work.
Edit. The question is not "how to write that metafunction" but "how do I catch that double inheritance error and output false_type when it happens". AFAIK, it's possible only with SFINAE.
template <typename T>
struct dummy {};
// error: duplicate base type ‘dummy<int>’ invalid
template <typename T, typename U>
struct fail : dummy<T>, dummy<U> {};
template <typename T>
true_type test(fail<T, T> a = fail<T, T>());
false_type test(...);
int main() {
cout << decltype(test<int>())::value << endl;
}
Live version here.
Edit. Previously I've tried to do this with specialization failure, but it didn't work either with the same compilation error.
template <typename T>
struct dummy {};
template <typename T, typename U>
struct fail : dummy<T>, dummy<U>, true_type {};
template <typename T, typename U = void>
struct test : false_type {};
template <typename T>
struct test<T, typename enable_if<fail<T, T>::value, void>::type> : true_type {};
Live version here.
You can't catch duplicate inheritance with SFINAE, because it is not one of the listed reasons for deduction to fail under 14.8.2p8 [temp.deduct]; equally, it is because the error occurs outside the "immediate context" of template deduction, as it is an error with the instantiation of your struct fail.
There is however a very similar technique which is suitable for use in your case, which is to detect an ambiguous conversion from a derived class to multiple base classes. Clearly the ambiguous base classes can't be inherited directly from a single derived class, but it works fine to inherit them in a linear chain:
C<> A<int>
| /
C<int> A<char>
| /
C<char, int> A<int>
| /
C<int, char, int>
Now a conversion from C<int, char, int> to A<int> will be ambiguous, and as ambiguous conversion is listed under 14.8.2p8 we can use SFINAE to detect it:
#include <type_traits>
template<class> struct A {};
template<class... Ts> struct C;
template<> struct C<> {};
template<class T, class... Ts> struct C<T, Ts...>: A<T>, C<Ts...> {};
template<class... Ts> void f(A<Ts>...);
template<class... Ts> std::false_type g(...);
template<class... Ts> decltype(f((A<Ts>(), C<Ts...>())...), std::true_type()) g(int);
template<class... Ts> using distinct = decltype(g<Ts...>(0));
static_assert(distinct<int, char, float>::value, "!!");
static_assert(!distinct<int, char, int>::value, "!!");
THE ERROR
prog.cpp: In instantiation of ‘struct fail<int, int>’:
prog.cpp:23:29: required from here
prog.cpp:9:8: error: duplicate base type ‘dummy<int>’ invalid
struct fail : dummy<T>, dummy<U> {};
As stated in the above diagnostic you cannot explicitly inherit from the same base more than once in a base-specifier-list, and since T and U can possibly be of the same type.. BOOM.
WAIT, HOLD UP; WHAT ABOUT SFINAE?
SFINAE is only checked in the immediate context of the template itself, error that happens beyond the declaration are not suitable to trigger a SFINAE.
14.8.2p8 Template argument deduction [temp.deduct]
If a substitution results in an invalid type or expression, type deduction fails. An invalid type or expression is one that would be ill-formed, with a diagnostic required, if written using the substituted arguemts.
Only invalid types and expressions in the immediate context of the function type and its templat eparameter types can result in a deduction failure.
The ill-formed inheritance does not happen in the immediate context, and the application is ill-formed.
To answer your question explicitly: since inheritance never happens in the declaration of a certain function, the ill-formed inheritance itself cannot be caught by SFINAE.
Sure, you can ask for the compiler to generate a class that uses inheritance, by instantiation it in the function declaration, but the actual (ill-formed) inheritance is not in the immediate context.
As I understand, you want a traits to check if all type are differents,
following may help:
#include <type_traits>
template <typename T, typename ...Ts> struct is_in;
template <typename T> struct is_in<T> : std::false_type {};
template <typename T1, typename T2, typename ... Ts>
struct is_in<T1, T2, Ts...> : std::conditional<std::is_same<T1, T2>::value, std::true_type, is_in<T1, Ts...>>::type {};
template <typename ... Ts> struct are_all_different;
template <> struct are_all_different<> : std::true_type {};
template <typename T> struct are_all_different<T> : std::true_type {};
template <typename T1, typename T2, typename ... Ts> struct are_all_different<T1, T2, Ts...> :
std::conditional<is_in<T1, T2, Ts...>::value, std::false_type, are_all_different<T2, Ts...>>::type {};
static_assert(are_all_different<char, int, float, long, short>::value, "type should be all different");
static_assert(!are_all_different<char, int, float, char, short>::value, "type should not all different");
What about a simple std::is_same<>? As far as I can see, it directly models the desired behaviour of your class.
So try something like this:
template<typename T, typename U>
fail
{
fail(const T &t, const U &u)
{
static_assert(std::is_same<T,U>::value,"T and U must have a distinct type");
}
};
Or even better, directly use std::is_same<T,U> in your code.
EDIT: Here is a solution inspired by Jarod42's but which uses only a single class (to make it clearer: this is an answer to the question how to write a variadic class template which detects whether all given types are distinct, which seemed to be the desired goal in one of the early versions of the original question):
#include <type_traits>
template <typename ...Ts> struct are_all_different {};
template <> struct are_all_different<> {static const bool value=true;};
template <typename T> struct are_all_different<T> {static const bool value=true;};
template <typename T1, typename T2>
struct are_all_different<T1, T2>
{
static const bool value = !std::is_same<T1, T2>::value;
};
template <typename T1, typename T2, typename ...Ts>
struct are_all_different<T1,T2,Ts...>
{
static const bool value = are_all_different<T1, T2>::value
&& are_all_different<T1, Ts...>::value
&& are_all_different<T2, Ts...>::value;
};
static_assert(are_all_different<char, int, float, long, short>::value, "type should be all different");
static_assert(!are_all_different<char, int, float, char, short>::value, "type should not all different");
In the end, for n variadic template arguments, this should check for equality of all of the n*(n+1)/2 combinations.
I don’t really understand what you are trying to achieve, but I can help you with the error
template <typename T, typename U>
struct fail : dummy<T>, dummy<U> {};
template <typename T>
struct fail<T, T> : dummy<T> {};
The error is because when you instantiate fail with T and U the same you basically inherit from the same class twice, which is illegal, so you need to create a specialization to take care of this case.
Related
I have some experience with std::enable_if. IIRC, it is about if a well-formed expression results in true return back the user type T (if given) or void via nested type alias.
template<bool,typename = void>
struct enable_if;
template<typename T>
struct enable_if<true,T>{
using type = T;
};
template <typename T, typename = void>
struct base_template{
enum { value= false};
};
template <typename T>
struct base_template<T, typename enable_if<std::is_integral<T>::value>::type> {
enum { value= true};// something useful...
};
struct some{};
static_assert(base_template<some>::value,"F*"); //error: static assertion failed: F
static_assert(base_template<int>::value,"F*");
But in boost.Hana I see this trait when<> and its implementation is like
template <bool condition>
struct when;
template <typename T, typename = when<true>>
struct base_template{
enum { value= false};
};
template <typename T>
struct base_template<T, when<std::is_integral<T>::value>> {
enum { value= true};// something useful...
};
struct some{};
static_assert(base_template<int>::value,"F*");
static_assert(base_template<some>::value,"F*");<source>:28:15: error: static assertion failed: F*
How the SFINAE works here? though the std::is_integral<some>::value is going to result in false, it doesn't mean(it does?) that the when<false> is ill-formed and should dispatch the instantiation to the primary class template. Am I missing anything here?
It's the same general idea. You can use enable_if_t or decltype in basically the same way. Now, you're probably used to seeing SFINAE partial specializations like this:
template<class T, class U = void>
struct Foo {};
template<class T>
struct Foo<T, decltype(T::bar())> {};
... Foo<X> ...
Here, Foo<X> is first expanded by the compiler to Foo<X, void> (because you didn't provide U at the "call site", so the default U = void is filled in instead). Then, the compiler looks for the best-matching specialization of class template Foo. If decltype(X::bar()) is in fact void, then Foo<T, decltype(T::bar())> [with T=X] will be a perfect match for Foo<X, void>. Otherwise, the generic Foo<T, U> [with T=X, U=void] will be used instead.
Now for the Hana when example.
template<bool> struct when {};
template<class T, class U = when<true>>
struct Foo {};
template<class T>
struct Foo<T, when<T::baz>> {};
... Foo<X> ...
Here, Foo<X> is first expanded by the compiler to Foo<X, when<true>> (because you didn't provide U at the "call site", so the default U = when<true> is filled in instead). Then, the compiler looks for the best-matching specialization of class template Foo. If when<X::baz> is in fact when<true>, then Foo<T, when<T::baz>> [with T=X] will be a perfect match for Foo<X, when<true>>. Otherwise, the generic Foo<T, U> [with T=X, U=when<true>] will be used instead.
You can replace the simple expression T::baz in my example with any arbitrarily complicated boolean expression, as long as it's constexpr-evaluable. In your original example, the expression was std::is_integral<T>::value.
My CppCon 2017 session "A Soupçon of SFINAE" walks through some similar examples.
We have this little metaprogramming marvel called std::conditional described here. In the same reference it says that a possible implementation is
template<bool B, class T, class F>
struct conditional { typedef T type; };
template<class T, class F>
struct conditional<false, T, F> { typedef F type; };
So if in code I do something like
typename std::conditional<true,int,double>::type a;
the compiler will follow the first definition and if I do something like
typename std::conditional<false,int,double>::type b
the compiler will take the second. Why does that work ? What compilation rule is in place here ?
In short it has to do with template specialization rules. These are like function overloads but for types.
Here compiler prefers a more specialized type to the more general type.
template<bool B, class T, class F>
struct conditional { typedef T type; };
template<class T, class F>
struct conditional<false, T, F> { typedef F type; };
So if a template instantiation coditional<false, ...> is seen by compiler it finds:
the general template template<bool B, class T, class F> struct conditional
all its specializations: template<class T, class F> struct conditional<false, T, F>
At that point it tries to match as many specialized arguments as possible and ends up selecting the specialization with false.
For example introducing another version like:
template<class F>
struct conditional<false, int, F> { typedef int type; };
and instantiating a template type like conditional<false, int, double> will prefer specialization
template<class F> struct conditional<false, int, F>
to
template<class T, class F> struct conditional<false, T, F> which is more general compared to the version with 2 specialized paramaters.
Some tricks at this point:
It is even OK to just declare the most generic case (i.e. generic form of the template is just declared but not defined) and only have specializations for cases you really intend to have. All non-specialized cases will result in compile errors and asking the user to specialize the case for a particular type.
Why does that work ? What compilation rule is in place here ?
I'm not an expert but I'll try to explain from the pratical point of view.
Hoping to use the rights terms...
With
template <bool B, class T, class F>
struct conditional { typedef T type; };
(but, with C++11, I prefer
template <bool B, typename T, typename>
struct conditional { using type = T; };
) you declare the template
template <bool, typename, typename>
struct conditional;
and define the generic (not specialized) version.
With
template< class T, class F>
struct conditional<false, T, F> { typedef F type; };
(or, in C++11,
template <typename T, typename F>
struct conditional<false, T, F> { using type = F; };
) you define a partial specialization of conditional
When the template arguments of a class (or struct) match two or more definitions, the compliler choose the more specialized one; so, for
typename std::conditional<false,int,double>::type
both definitions of the class match so the compiler choose the specialized one (the specialized with false) anche type is double.
For
typename std::conditional<true,int,double>::type a;
only the generic version match, so type is int.
In most of the template meta programming main rule at play here is SFINAE(Substitution failure is not an error). According to this rule if compiler not find the type then instead of throwing an error it moves forward and check if there is any future statement that can provide type information.
I want to implement a has_no_duplicates<...> type trait that evaluates to std::true_type if the passed variadic type list has no duplicate types.
static_assert(has_no_duplicates<int, float>{}, "");
static_assert(!has_no_duplicates<float, float>{}, "");
Let's assume, for the scope of this question, that I want to do that using multiple inheritance.
When a class inherits from the same type more than once, an error occurs.
template<class T>
struct type { };
template<class... Ts>
struct dup_helper : type<Ts>... { };
// No errors, compiles properly.
dup_helper<int, float> ok{};
// Compile-time error:
// base class 'type<float>' specified more than once as a direct base class
dup_helper<float, float> error{};
I assumed I could've used void_t to "detect" this error, but I couldn't implement a working solution following the code samples from cppreference.
This is what I tried:
template<class, class = void>
struct is_valid
: std::false_type { };
// First try:
template<class T>
struct is_valid<T, std::void_t<decltype(T{})>>
: std::true_type { };
// Second try:
template<class T>
struct is_valid<T, std::void_t<T>>
: std::true_type { };
For my third try, I tried delaying the expansion of dup_helper<...> using a wrapper class that took dup_helper as a template template parameter, like wrapper<dup_helper, ...> and expanded it inside of void_t.
Unfortunately, all my tries resulted in the aforementioned error always preventing compilation.
I assume this type of error is not detectable as a "substitution failure", but I'd like confirmation.
Is this kind of error actually impossible to detect using void_t? (Will it always result in a compilation failure?)
Is there a way to detect it without causing compilation to fail? (Or a non-void_t workaround that still makes use of the "multiple inheritance trick")?
As #Canoninos noted, the problem is that:
it isn't the declaration of dup_helper<T, T> which causes an error but its definition [...].
Or, in Standardese, the error occurs outside the "immediate context" ([temp.deduct]) of the substitution:
8 - [...] Only invalid types and expressions in the immediate context of the function type and
its template parameter types can result in a deduction failure. [ Note: The evaluation of the substituted types
and expressions can result in side effects such as the instantiation of class template specializations and/or
function template specializations, the generation of implicitly-defined functions, etc. Such side effects are
not in the “immediate context” and can result in the program being ill-formed. — end note ]
Here the error occurs while instantiating dup_helper<float, float> so is not in the "immediate context".
One multiple inheritance trick that's very close to yours involves adding an extra layer of inheritance, by indexing the multiple bases:
helper<<0, 1>, <float, float>>
+
+----+----+
v v
ix<0, float> ix<1, float>
+ +
v v
t<float> t<float>
This gives us a helper class with a valid definition and that can be instantiated but not cast to its ultimate base classes, because of ambiguity:
static_cast<t<float>>(helper<...>{}); // Error, SFINAE-usable
Example.
That's my solution using meta-programming and type-list idiom.
I use this code as part of my library implementing reflection for C++. I think there is no need in void_t or inheritance at all to solve this task.
template <typename ...Args>
struct type_list
{};
using empty_list = type_list<>;
// identity
template<typename T>
struct identity
{
using type = T;
};
// is_typelist
template<typename T>
struct is_typelist: std::false_type
{};
template<typename ...Args>
struct is_typelist<type_list<Args...>>: std::true_type
{};
template<typename T>
struct check_typelist
{
using type = void;
static constexpr void *value = nullptr;
static_assert(is_typelist<T>::value, "T is not a type_list!");
};
// indexof
namespace internal {
template<typename T, typename V, std::int64_t index>
struct typelist_indexof_helper: check_typelist<T>
{};
template<typename H, typename ...T, typename V, std::int64_t index>
struct typelist_indexof_helper<type_list<H, T...>, V, index>:
std::conditional<std::is_same<H, V>::value,
std::integral_constant<std::int64_t, index>,
typelist_indexof_helper<type_list<T...>, V, index + 1>
>::type
{};
template<typename V, std::int64_t index>
struct typelist_indexof_helper<empty_list, V, index>: std::integral_constant<std::int64_t, -1>
{};
}
template<typename T, typename V>
using typelist_indexof = ::internal::typelist_indexof_helper<T, V, 0>;
template<typename T, typename V>
struct typelist_exists: std::integral_constant<bool, typelist_indexof<T, V>::value >= 0>
{};
// remove_duplicates
namespace internal {
template<typename P, typename T>
struct typelist_remove_duplicates_helper: check_typelist<T>
{};
template<typename ...P, typename H, typename ...T>
struct typelist_remove_duplicates_helper<type_list<P...>, type_list<H, T...>>:
std::conditional<typelist_exists<type_list<T...>, H>::value,
typelist_remove_duplicates_helper<type_list<P...>, type_list<T...>>,
typelist_remove_duplicates_helper<type_list<P..., H>, type_list<T...>>
>::type
{};
template<typename ...P>
struct typelist_remove_duplicates_helper<type_list<P...>, empty_list>: identity<type_list<P...>>
{};
}
template<typename T>
using typelist_remove_duplicates = ::internal::typelist_remove_duplicates_helper<empty_list, T>;
template<typename ...Args>
struct has_no_duplicates: std::integral_constant<bool, std::is_same<type_list<Args...>,
typename typelist_remove_duplicates<type_list<Args...>>::type>::value>
{};
DEMO
I have a problem similar to this one: SFINAE tried with bool gives compiler error: "template argument ‘T::value’ involves template parameter"
I want to define a trait that tells if a complex representation is an array of structs, where real values are never AoS hence no user defined specilization should be required, but for complex you'd always need one:
/**
* Evaluates to a true type if the given complex type is an Array of Structs, false otherwise
* Defaults to false for Real values
*/
template< typename T, bool T_isComplex = IsComplex<T>::value >
struct IsAoS: std::false_type{};
/**
* Undefined for (unknown) complex types
*/
template< typename T >
struct IsAoS< T, true >;
Specializations are done in the std-way by deriving from std::true/false_type.
The problem is: With this implementation I get the "template argument involves template parameter(s)" error (which is explained in the linked question) but if i switch to types (ommit "::value" in first template and change true to true_type in 2nd) Complex types won't match the 2nd template anymore because the only derive from std::true_type and ARE NOT std::true_type.
Only solution I can think of is using expression SFINAE and change the 2nd parameter of the main template to default void and an enable_if for the real case (isComplex==false). Anything better?
On the assumption that IsAoS<T>'s desired behavior is:
If IsComplex<T>::value is false, it defaults to false_type;
Otherwise, it is an error unless the user provides a specialization.
It's straightforward to implement with a static_assert; no default template argument needed:
template< typename T >
struct IsAoS: std::false_type {
static_assert(!IsComplex<T>::value, "A user specialization must be provided for Complex types");
};
If you want the default argument trickery, just use a wrapper layer:
namespace detail {
template< typename T, bool T_isComplex = IsComplex<T>::value >
struct IsAoS_impl: std::false_type{};
/**
* Undefined for (unknown) complex types
*/
template< typename T >
struct IsAoS_impl< T, true >;
}
template<class T> struct IsAoS : detail::IsAoS_impl<T> {};
Users should just specialize IsAoS.
If I understand correctly, you want:
template<class> struct IsComplex_impl {using type = std::false_type;};
template<class T> struct IsComplex_impl<std::complex<T>> {using type = std::true_type;};
template <typename T>
using IsComplex = typename IsComplex_impl<T>::type;
// Declaration only
template<typename T, typename T_isComplex = IsComplex<T>>
struct IsAoS;
// general case
template< typename T >
struct IsAoS< T, std::false_type >: std::false_type{};
// specialization for complex<double>
template<>
struct IsAoS< std::complex<double>>: std::true_type{};
Live Demo
or with same signature:
template<class> struct IsComplex_impl : std::false_type {};
template<class T> struct IsComplex_impl<std::complex<T>> : std::true_type {};
template <typename T>
constexpr bool IsComplex() {return IsComplex_impl<T>::value;}
// Declaration only
template<typename T, bool T_isComplex = IsComplex<T>()>
struct IsAoS;
// general case
template< typename T >
struct IsAoS< T, false>: std::false_type{};
// specialization for complex<double>
template<>
struct IsAoS< std::complex<double>>: std::true_type{};
Live Demo
I'm trying to partially specialize a trait for arrays of non-chars:
template<typename T>
struct is_container : std::false_type {};
template<typename T, unsigned N>
struct is_container<T[N]>
: std::enable_if<!std::is_same<T, char>::value, std::true_type>::type {};
Visual Studio 2010 gives me a C2039 (type is no element of enable_if...). However, shouldn't SFINAE just bottom out here instead of giving a compiler error? Or does SFINAE not apply in this case?
Of course I could just separate the specializations for non-char and char:
template<typename T>
struct is_container : std::false_type {};
template<typename T, unsigned N>
struct is_container<T[N]> : std::true_type {};
template<unsigned N>
struct is_container<char[N]> : std::false_type {};
But I would really like to know why SFINAE doesn't work in this particular case.
Check the topic '3.1 Enabling template class specializations' at
http://www.boost.org/doc/libs/1_47_0/libs/utility/enable_if.html
Edit: in case boost.org link dies...
3.1 Enabling template class specializations
Class template specializations can be enabled or disabled with enable_if. One extra template parameter needs to be added for the enabler expressions. This parameter has the default value void. For example:
template <class T, class Enable = void>
class A { ... };
template <class T>
class A<T, typename enable_if<is_integral<T> >::type> { ... };
template <class T>
class A<T, typename enable_if<is_float<T> >::type> { ... };
Instantiating A with any integral type matches the first specialization, whereas any floating point type matches the second one. All other types match the primary template. The condition can be any compile-time boolean expression that depends on the template arguments of the class. Note that again, the second argument to enable_if is not needed; the default (void) is the correct value.