detection idiom: why does the condition have to be a `using` directive? - c++

EDIT: this other question of mine focuses on a reduced version of this problem, possibly easier to understand.
I wrote a small snippet that reproduces the behaviour of std::experimental::is_detected (here). My implementation is basically taken from cppreference but I got rid of the Default template parameter.
My question is: in the following snippet, why does has_type (the condition to be checked) have to be a using declaration and cannot be, e.g. a struct (in which case is_detected returns a wrong result)?
/***** is_detected definition *****/
template<typename...Args>
using void_t = void;
template<typename Void, template<class...> class Op, typename ...Args>
struct Detector {
static constexpr bool value = false;
};
template<template<class ...> class Op, typename ...Args>
struct Detector<void_t<Op<Args...>>, Op, Args...> {
static constexpr bool value = true;
};
template<template<class...> class Op, typename...Args>
using is_detected_t = Detector<void, Op, Args...>;
/****************************/
/***** is_detected test *****/
// two dummy types on which to test a condition
struct Yes { using type = void; };
struct No { };
// the condition to test
template<typename T>
using has_type = typename T::type;
// struct has_type { using type = typename T::type; }; // does not work as intended!
int main() {
static_assert(is_detected_t<has_type, Yes>::value, "");
static_assert(!is_detected_t<has_type, No>::value, "");
return 0;
}

It might help to look at how has_type is actually used by the detector:
template<template<class ...> class Op, typename ...Args>
struct Detector<void_t< Op<Args...>>, Op, Args...> {
// ^^ ^^
// has_type Yes/No
static constexpr bool value = true;
};
For this specialization to match the compiler must make sure that Op<Args...>, when replacing the parameters (Op and Args...) with the actual arguments (has_type and Yes/No), must name a type (since that's what the template void_t requires as first template argument).
Since has_type is not a type, but rather an alias of some type, it must look whether whatever is aliased names a type.
For Yes this will be Yes::type, which again is an alias of void. void is a type, so everything is fine, the specialization matches, value is true.
For No this will be No::type, which does not exist (No has no member type after all). Thus, the substitution fails (but this is not an error, SFINAE), the specialization cannot be used. Thus the compiler chooses the base template, where value is false.
Now what happens when you define has_type as follows:
template<typename T>
struct has_type { using type = typename T::type; }
Then above specialization needs (in the No case) that a type has_type<No> exists. has_type is a class template, which given some type (No is a type, so everything good) "produces" a type. Thus, has_type<No> is a type. Thus the specialization matches, value is true.
The members of has_type<No> are not needed at this point. You could even use template<typename> struct has_type; (only a declaration, no definition). In other words, it may be an incomplete type:
A template argument for a type template parameter must be a type-id, which may name an incomplete type [..]
http://en.cppreference.com/w/cpp/language/template_parameters
The contents only matter when the compiler actually needs them, e.g. for creating an object of that type:
// Class template with some random members.
template<typename T>
struct Foo {
using baz = typename T::baz;
constexpr static int value = T::value * 42;
};
// Class template which is even only declared
template<typename X> struct Bar; // no definition
// Does not use its template parameter in any way. Needs just a type name.
template<typename> struct Defer {};
int main() {
Defer<Foo<int>> ok;
Defer<Bar<int>> ok_too;
// Foo<int> fail;
// Bar<int> fail_too;
return 0;
}
This mechanism is often used for "type tags", which e.g. can be used to create different types with identical "content" from a single template:
template<typename /* TAG */, typename ValueType>
struct value_of_strong_type {
ValueType value;
// ...
};
struct A_tag; // no definition
using A = value_of_strong_type<A_tag, int>;
struct B_tag; // no definition
using B = value_of_strong_type<B_tag, int>;
Both A and B behave identically, but are not convertible to each other, because they're completely different types.
To make the detector work with such class templates as you showed you need the following specialization:
template<template<class ...> class Op, typename ...Args>
struct Detector<void_t<typename Op<Args...>::type>, Op, Args...> {
// ^^^^^^^^ ^^^^^^
static constexpr bool value = true;
};
Though you cannot just add it, otherwise you run into ambiguous resolution errors.

Related

Select member type of class template based on template type?

I have a class template that has members of some type. This type is determined based on the type that is provided when instantiating the template. It uses a default (double in the example below) unless an override is provided by that class. Classes used as template types may provide this override type (here "Two" provides the override type "int"). If a class provides the override, the override should only be used if the class also sets the UseOverride flag. If flag is absent or false, default "double" should be used.
Problem is that if the template type does not provide the "type", then compiler gives error in below code. I suspect I need to use SFINAE here, but haven't been able to figure out a suitable approach for it, even after puzzling and browsing related questions for a good part of the afternoon.
How to define the EventType template so that it works as intended? I want to keep the EventType<T> syntax.
#include <iostream>
struct One {
//This type is ignored, and double is used, because UseOverride = true is not specified:
using type = short;
};
struct Two {
static constexpr bool UseOverride = true;
using type = int;
};
struct Three {
static constexpr bool UseOverride = false;
//I don't want the compiler to complain that "type" is not available here (because it should default to double anyhow since
//the class instructs not to use the override). But compile does generate error.
//How to avoid this?
};
template <typename T, typename = void>
struct overrideInfoProvided : std::false_type {};
template <typename T>
struct overrideInfoProvided<T, decltype((void)T::UseOverride, void())> : std::true_type {};
template <typename T>
constexpr bool Override()
{
if constexpr (overrideInfoProvided<T>::value)
{
return T::UseOverride;
}
return false;
}
template<class T>
using EventType = typename std::conditional_t<Override<T>(), typename T::type, double>;
template <class T>
struct Test
{
typename EventType<T> member;
Test()
{
std::cout << member << std::endl;
}
};
int main()
{
Test<One>();
Test<Two>();
//Gives error:
//Test<Three>();// `type': is not a member of any direct or indirect base class of `three';
}
I don't want the compiler to complain that "type" is not available here (because it should default to double anyhow since the class instructs not to use the override). But compiler does generate error. How to avoid this?
Just defer the access to ::type with the below type_identity helper:
template <typename T>
struct type_identity { using type = T; };
template <typename T>
using EventType = typename std::conditional_t<Override<T>()
, T
, type_identity<double>>::type;
// ~~~~~~~~~~~~^
DEMO
You are on the right track, but you don't need to have separate checks for the existence of useOverride, and type. Instead, you can do both of the checks in the same sfinae class:
template <typename T, typename = void, typename = void>
struct EventType_T {
using t = double; // default if useOverride or type doesn't exist
};
template <typename T>
struct EventType_T<T, std::void_t<decltype(T::UseOverride)>,
std::void_t<typename T::type>> {
// choose type if useOverride is true, and double otherwise
using t = std::conditional_t<T::UseOverride, typename T::type, double>;
};
template <typename T>
using EventType = typename EventType_T<T>::t;
Here's a demo. This allows you to still use the EventType<T> syntax as before.
Note, the t member instead of type is unconventional, but since we are already testing for a type member in T, this might make it clearer what's going on. I would recommend using type once yo understand how the solution works.

Find out if type is callable

I need to figure out if template argument is a callable object with non-void return value.
I've defined the following:
template<class T, class U = T>
struct is_callable
{
constexpr static const bool val = false;
};
template<class T>
struct is_callable<T, std::result_of_t<T()>>
{
constexpr static const bool val = true;
};
template<class T>
constexpr bool is_func = is_callable<T>::val;
But the following variables all are false
auto lambda = []() {return 3.0; };
auto intIsFunc = is_func<int>; //false -- ok
auto functionIsFunc = is_func<std::function<int()>>; //false -- wrong
auto lambdaIsFunc = is_func<decltype(lambda)>; //false -- wrong
What is wrong with the code?
How to improve the is_func to return true not only on callable objects, but callable with return type which is constructible (use somewhere std::is_constructible_v<>)?
Use enable_if
template <typename T, typename = void>
struct is_callable_non_void_return : public std::false_type {};
template <typename T>
struct is_callable_non_void_return<
T,
std::enable_if_t<!std::is_same_v<void, std::result_of_t<T()>>>>
: public std::true_type {};
This works because of SFINAE: Substitution Failure Is Not An Error.
The compiler will see the second is_callable_non_void_return as a specialization of the first, and try to match the template by instantiating the enable_if: first result_of_t, then is_same_v. If either fails, then a substitution failure occurs and the compiler falls back to the general case.
You misunderstand what template specialization means.
template<class T, class U = T>
struct is_callable
this is the primary specialization.
When you do is_callable<Foo> it means you are typing is_callable<Foo, Foo>. Nothing you co in other specializations can change this.
template<class T>
struct is_callable<T, std::result_of_t<T()>>
this attemptes to match against the arguments passed, it never changes them.
So for is_callable<Foo> this is is_callable<Foo,Foo>. Matching T against Foo is easy; so now we have T=Foo. We then look at the ones that are dependent on T -- std::result_of_t<T()> aka std::result_of_t<Foo()>. This evaluates to the result of calling Foo with () (roughly).
If we had:
struct Foo {
Foo operator(){ return {}; }
};
then std::result_of_t<Foo()> is Foo, and the specialization matches!
But if we have Foo=std::function<void()> then the result of () is void coming out of std::result_of_t<Foo()>.
Thus we have is_callable<Foo, void> matching against is_callable<Foo, Foo>. This obviously doesn't match as Foo does not equal void.
template<class T, class U = void>
struct is_callable
// body unchanged
notice the U=void instead of the U=T.
template<class T>
struct is_callable<T, std::void_t<std::result_of_t<T()>>>
// body unchanged
and here we use void_t.
std::void_t takes any types passed to it and produces void. Now let us go through the same exercise with is_callable< std::function<int()> >.
Under the primary specialization this becomes
is_callable<std::function<int()>, void>
we then try to match it against the specialization:
template<class T>
struct is_callable<T, std::void_t<std::result_of_t<T()>>>
again, T=std::function<int()> arrives immediately. The second clause is in a dependent context, so we don't pattern match it.
struct is_callable<std::function<int()>, std::void_t<std::result_of_t<std::function<int()>()>>>
struct is_callable<std::function<int()>, std::void_t<int>>
struct is_callable<std::function<int()>, void>
and wow, this matches the types passed to the template!
std::enable_if uses a trick whereby if the first argument is true, it returns the second argument (defaulting to void). If the first argument is false, it is a substitution failure.
How to improve the is_func to return true not only on callable objects, but callable with return type which is constructible (use somewhere std::is_constructible_v<
I'll skip the bodies:
template<class T, class=void>
struct is_callable
// ...
template<class T>
struct is_callable<T, std::enable_if_t<
std::is_constructible_v< std::result_of_t<T()> >
>>
now, for a type T for which T() is not constructible, the specialization fails to match because in computing the 2nd argument we get a substitution failure. And when it is constructible, we get void.

In C++03, how to conditionally declare a static data member of a template class, based on type traits

Background
Consider that I would like to, in C++03, implement the following contrived construct:
(+)
template< typename T > // parameter list must contain T but may otherwise modified
struct foo;
If the type trait dummy_trait<T>::value over the template parameter
T of foo is true, then foo should contain a static data member
named value, of type T, initialized with the integer literal expression 42; otherwise there should be no static data
member named value.
with e.g. the following dummy_trait (actual dummy trait may differ):
template< typename T >
struct dummy_trait { static const bool value = false; };
template<>
struct dummy_trait<int> { static const bool value = true; };
Question(s)
Are there any other (possibly more appropriate) alternatives to implement (+), in addition to the two approaches below?
Are there any pitfalls or aspects to carefully bear in mind using any of the two approaches below?
Approaches of my own
Using the helper
template< bool B, typename T = void >
struct enable_if {};
template< typename T >
struct enable_if<true, T> { typedef T type; };
Either e.g. using partial class template specialization:
// (A)
template< typename T, typename Enable = void >
struct foo {};
template< typename T >
struct foo<T, typename enable_if<dummy_trait<T>::value>::type > {
static const T value = 42;
};
Or e.g. leveraging SFINAE introducing a conditional (on the trait) error on the declaration of the static data member:
// (B)
template< typename T >
struct foo {
static const typename enable_if<
dummy_trait<T>::value, T >::type value = 42;
};
If I'm not mistaken, (A) above would allow using foo also for instantiations with types T where dummy_trait<T>::value is false (although there would be no value member of foo), whereas such instantiations would not be possible at all if using (B).
Are there any other (possibly more appropriate) alternatives to implement (+), in addition to the two approaches below (leveraging SFINAE)?
Not a great difference from your solution (A) but... you can obtain the result through inheritance
template <typename T>
struct foo : public bar<T>
{ };
Where bar is as follows.
template <typename T, bool = dummy_trait<T>::value>
struct bar { };
template <typename T>
struct bar<T, true> { static const T value = 42; };
You can also see bar as simpler way (no enable_if) to obtain foo.
Are there any pitfalls or aspects to carefully bear in mind using any of the two approaches below?
The only pitfall I see is the one you have just seen: the (B) method doesn't works because you cant define foo when dummy_trait<T>::value is false.
I mean... (B) isn't SFINAE because substitution failure, in this case, is an error.

Can we use the detection idiom to check if a class has a member function with a specific signature?

Given a (reduced) implementation of the detection idiom
namespace type_traits
{
template<typename... Ts>
using void_t = void;
namespace detail
{
template<typename, template<typename...> class, typename...>
struct is_detected : std::false_type {};
template<template<class...> class Operation, typename... Arguments>
struct is_detected<void_t<Operation<Arguments...>>, Operation, Arguments...> : std::true_type {};
}
template<template<class...> class Operation, typename... Arguments>
using is_detected = detail::is_detected<void_t<>, Operation, Arguments...>;
template<template<class...> class Operation, typename... Arguments>
constexpr bool is_detected_v = detail::is_detected<void_t<>, Operation, Arguments...>::value;
}
we can easily check if a class foo contains a member function bar
struct foo {
int const& bar(int&&) { return 0; }
};
template<class T>
using bar_t = decltype(std::declval<T>().bar(0));
int main()
{
static_assert(type_traits::is_detected_v<bar_t, foo>, "not detected");
return 0;
}
However, as you can see, we cannot detect that foo::bar's argument type is int&&. The detection succeeds, cause 0 can be passed to foo::bar. I know that there are plenty of options to check for the exact signature of a (member) function. But I would like to know, if it's possible to modify this detection toolkit in order to detect that foo::bar's argument type is exactly int&&.
[I've created a live demo of this example.]
Without changing your type_traits, you may do
template<typename T, T> struct helper {};
template<class T>
using bar_t = decltype(helper<const int& (T::*)(int&&), &T::bar>{});
Demo
Adapting the ideas of dyp and Jarod42, I've came up with
template<class T, typename... Arguments>
using bar_t = std::conditional_t<
true,
decltype(std::declval<T>().bar(std::declval<Arguments>()...)),
std::integral_constant<
decltype(std::declval<T>().bar(std::declval<Arguments>()...)) (T::*)(Arguments...),
&T::bar
>
>;
Notice that bar_t will be the return type of a bar call. In this way, we stay consistent with the toolkit. We can detect the existence by
static_assert(type_traits::is_detected_v<bar_t, foo, int&&>, "not detected");
However, while this solution does exactly what I intended, I hate that I need to write "so much complicated code" for every method I want to detect. I've asked a new question targeting this issue.
I don't think this works for checking const qualifiers.
decltype(std::declval<T>().bar(std::declval<Arguments>()...)) (T::*)(Arguments...)
always produces a non-const function pointer type, whereas &T::bar will produce a const function pointer if bar is marked const.
This will then fail trying to convert the const pointer type to the non-const pointer type for storage in integral_constant.

Match type of inherited member functions

I have the following snipped of code, which does not compile.
#include <iostream>
struct A {
void foo() {}
};
struct B : public A {
using A::foo;
};
template<typename U, U> struct helper{};
int main() {
helper<void (A::*)(), &A::foo> compiles;
helper<void (B::*)(), &B::foo> does_not_compile;
return 0;
}
It does not compile since &B::foo resolves to &A::foo, and thus it cannot match the proposed type void (B::*)(). Since this is part of a SFINAE template that I am using to check for a very specific interface (I'm forcing specific argument types and output types), I would like for this to work independently of inheritances, while keeping the check readable.
What I tried includes:
Casting the second part of the argument:
helper<void (B::*)(), (void (B::*)())&B::foo> does_not_compile;
This unfortunately does not help as the second part is now not recognized as a constant expression, and fails.
I've tried assigning the reference to a variable, in order to check that.
constexpr void (B::* p)() = &B::foo;
helper<void (B::* const)(), p> half_compiles;
This code is accepted by clang 3.4, but g++ 4.8.1 rejects it, and I have no idea on who's right.
Any ideas?
EDIT: Since many comments are asking for a more specific version of the problem, I'll write it here:
What I'm looking for is a way to explicitly check that a class respects a specific interface. This check will be used to verify input arguments in templated functions, so that they respect the contract that those functions require, so that compilation stops beforehand in case the class and a function are not compatible (i.e. type traits kind of checking).
Thus, I need to be able to verify return type, argument type and number, constness and so on of each member function that I request. The initial question was the checking part of the bigger template that I'm using to verify matches.
A working solution to your problem as posted at https://ideone.com/mxIVw3 is given below - see also live example.
This problem is in a sense a follow-up of Deduce parent class of inherited method in C++. In my answer, I defined a type trait member_class that extracts a class from a given pointer to member function type. Below we use some more traits to analyse and then synthesize back such a type.
First, member_type extracts the signature, e.g. void (C::*)() gives void():
template <typename M> struct member_type_t { };
template <typename M> using member_type = typename member_type_t <M>::type;
template <typename T, typename C>
struct member_type_t <T C::*> { using type = T;};
Then, member_class extracts the class, e.g. void (C::*)() gives C:
template<typename>
struct member_class_t;
template<typename M>
using member_class = typename member_class_t <M>::type;
template<typename R, typename C, typename... A>
struct member_class_t <R(C::*)(A...)> { using type = C; };
template<typename R, typename C, typename... A>
struct member_class_t <R(C::*)(A...) const> { using type = C const; };
// ...other qualifier specializations
Finally, member_ptr synthesizes a pointer to member function type given a class and a signature, e.g. C + void() give void (C::*)():
template <typename C, typename S>
struct member_ptr_t;
template <typename C, typename S>
using member_ptr = typename member_ptr_t <C, S>::type;
template <typename C, typename R, typename ...A>
struct member_ptr_t <C, R(A...)> { using type = R (C::*)(A...); };
template <typename C, typename R, typename ...A>
struct member_ptr_t <C const, R(A...)> { using type = R (C::*)(A...) const; };
// ...other qualifier specializations
The two previous traits need more specialization for different qualifiers to be more generic, e.g. const/volatile or ref-qualifiers. There are 12 combinations (or 13 including data members); a complete implementation is here.
The idea is that any qualifiers are transferred by member_class from the pointer-to-member-function type to the class itself. Then member_ptr transfers qualifiers from the class back to the pointer type. While qualifiers are on the class type, one is free to manipulate with standard traits, e.g. add or remove const, lvalue/rvalue references, etc.
Now, here is your is_foo test:
template <typename T>
struct is_foo {
private:
template<
typename Z,
typename M = decltype(&Z::foo),
typename C = typename std::decay<member_class<M>>::type,
typename S = member_type<M>
>
using pattern = member_ptr<C const, void()>;
template<typename U, U> struct helper{};
template <typename Z> static auto test(Z z) -> decltype(
helper<pattern<Z>, &Z::foo>(),
// All other requirements follow..
std::true_type()
);
template <typename> static auto test(...) -> std::false_type;
public:
enum { value = std::is_same<decltype(test<T>(std::declval<T>())),std::true_type>::value };
};
Given type Z, alias template pattern gets the correct type M of the member pointer with decltype(&Z::foo), extracts its decay'ed class C and signature S, and synthesizes a new pointer-to-member-function type with class C const and signature void(), i.e. void (C::*)() const. This is exactly what you needed: it's the same with your original hard-coded pattern, with the type Z replaced by the correct class C (possibly a base class), as found by decltype.
Graphically:
M = void (Z::*)() const -> Z + void()
-> Z const + void()
-> void (Z::*)() const == M
-> SUCCESS
M = int (Z::*)() const& -> Z const& + int()
-> Z const + void()
-> void (Z::*)() const != M
-> FAILURE
In fact, signature S wasn't needed here, so neither was member_type. But I used it in the process, so I am including it here for completeness. It may be useful in more general cases.
Of course, all this won't work for multiple overloads, because decltype doesn't work in this case.
If you simply want to check the existence of the interface on a given type T, then there're better ways to do it. Here is one example:
template<typename T>
struct has_foo
{
template<typename U>
constexpr static auto sfinae(U *obj) -> decltype(obj->foo(), bool()) { return true; }
constexpr static auto sfinae(...) -> bool { return false; }
constexpr static bool value = sfinae(static_cast<T*>(0));
};
Test code:
struct A {
void foo() {}
};
struct B : public A {
using A::foo;
};
struct C{};
int main()
{
std::cout << has_foo<A>::value << std::endl;
std::cout << has_foo<B>::value << std::endl;
std::cout << has_foo<C>::value << std::endl;
std::cout << has_foo<int>::value << std::endl;
return 0;
}
Output (demo):
1
1
0
0
Hope that helps.
Here's a simple class that passes your tests (and doesn't require a dozen of specializations :) ). It also works when foo is overloaded. The signature that you wish to check can also be a template parameter (that's a good thing, right?).
#include <type_traits>
template <typename T>
struct is_foo {
template<typename U>
static auto check(int) ->
decltype( static_cast< void (U::*)() const >(&U::foo), std::true_type() );
// ^^^^^^^^^^^^^^^^^^^
// the desired signature goes here
template<typename>
static std::false_type check(...);
static constexpr bool value = decltype(check<T>(0))::value;
};
Live example here.
EDIT :
We have two overloads of check. Both can take a integer literal as a parameter and because the second one has an ellipsis in parameter list it'll never be the best viable in overload resolution when both overloads are viable (elipsis-conversion-sequence is worse than any other conversion sequence). This lets us unambiguously initialize the value member of the trait class later.
The second overload is only selected when the first one is discarded from overload set. That happens when template argument substitution fails and is not an error (SFINAE).
It's the funky expression on the left side of comma operator inside decltype that makes it happen. It can be ill-formed when
the sub-expression &U::foo is ill-formed, which can happen when
U is not a class type, or
U::foo is inaccesible, or
there is no U::foo
the resulting member pointer cannot be static_cast to the target type
Note that looking up &U::foo doesn't fail when U::foo itself would be ambiguous. This is guaranteed in certain context listed in C++ standard under 13.4 (Address of overloaded function, [over.over]). One such context is explicit type conversion (static_cast in this case).
The expression also makes use of the fact that T B::* is convertible to T D::* where D is a class derived from B (but not the other way around). This way there's no need for deducing the class type like in iavr's answer.
value member is then initialized with value of either true_type or false_type.
There's a potential problem with this solution, though. Consider:
struct X {
void foo() const;
};
struct Y : X {
int foo(); // hides X::foo
};
Now is_foo<Y>::value will give false, because name lookup for foo will stop when it encounters Y::foo. If that's not your desired behaviour, consider passing the class in which you wish to perform lookup as a template parameter of is_foo and use it in place of &U::foo.
Hope that helps.
I suggest using decltype to generically determine the type of the member function pointers:
helper<decltype(&A::foo), &A::foo> compiles;
helper<decltype(&B::foo), &B::foo> also_compiles;
It may seem like a DRY violation, but repeating the name is fundamentally no worse than specifying the type separately from the name.