I'm asking about a variation of a (popular) question - detecting the existence of a method of a class.
I've read many answers here in SO, and most of the (post C++17) solutions look like this:
#include <type_traits>
template<class ...Ts>
struct voider{
using type = void;
};
template<class T, class = void>
struct has_foo : std::false_type{};
template<class T>
struct has_foo<T, typename voider<decltype(std::declval<T>().foo())>::type> : std::true_type{};
Basically, we're letting the compiler decide using a "trick" :
if the expression std::declval<T>().foo() is well-formed,
then decltype(std::declval<T>().foo()) doesn't produce a compiler-error,
then the compiler "prefers" has_foo<T, typename voider<decltype(...)>>::type since it doesn't need to replace the second template type with a default type.
great, but how can we combine noexcept with it?
I've tried many ways but it seems most techniques (including decltype(declval<type>.my_func()))
only care about the name, return type and the argument types and not about the noexcept.
You can do it with the help of noexpect operator (since C++11).
The noexcept operator performs a compile-time check that returns true if an expression is declared to not throw any exceptions.
e.g.
template<class T>
struct has_foo<T,
typename voider<decltype(std::declval<T>().foo()),
std::enable_if_t<noexcept(std::declval<T>().foo())>
>::type
> : std::true_type{};
LIVE
Related
Since C++20 concepts aren't standardized yet, I'm using static_assert as a makeshift concept check, to provide helpful error messages if a type requirement isn't met. In this particular case, I have a function which requires that a type is callable before getting its result type:
template <typename F, typename... Args>
void example() {
static_assert(std::is_invocable_v<F, Args...>, "Function must be callable");
using R = std::invoke_result_t<F, Args...>;
// ...
}
In addition, I require that the callable's result must be some kind of std::optional, but I don't know what type the optional will hold, so I need to get that type from it:
using R = // ...
using T = typename R::value_type; // std::optional defines a value_type
However, this will fail if type R doesn't have a value_type, e.g. if it's not a std::optional as expected. I'd like to have a static_assert to check for that first, with another nice error message if the assertion fails.
I could check for an exact type with something like std::is_same_v, but in this case I don't know the exact type. I want to check that R is some instance of std::optional, without specifying which instance it must be.
One way to do that is with a helper trait:
template <typename T>
struct is_optional { static constexpr bool value = false; };
template <typename T>
struct is_optional<std::optional<T>> { static constexpr bool value = true; };
template <typename T>
constexpr bool is_optional_v = is_optional<T>::value;
…and then I can write:
static_assert(is_optional_v<R>, "Function's result must be an optional");
That works, but it seems a little awkward to pollute my namespace with a helper trait just for a one-off check like this. I don't expect to need is_optional anywhere else, though I can imagine possibly ending up with other one-off traits like is_variant or is_pair too.
So I'm wondering: is there a more concise way to do this? Can I do the pattern matching on instances of std::optional without having to define the is_optional trait and its partial specialization?
Following the suggestion by several respondents, I made a re-usable trait:
template <typename T, template <typename...> typename Tpl>
struct is_template_instance : std::false_type { };
template <template <typename...> typename Tpl, typename... Args>
struct is_template_instance<Tpl<Args...>, Tpl> : std::true_type { };
template <typename T, template <typename...> typename Tpl>
constexpr bool is_template_instance_v = is_template_instance<T, Tpl>::value;
…so that I can write:
static_assert(is_template_instance_v<R, std::optional>, "Function's result must be an optional");
This is just as many lines and declarations as the is_optional trait, but it's no longer a one-off; I can use the same trait for checking other kinds of templates (like variants and pairs). So now it feels like a useful addition to my project instead of a kluge.
Can I do the pattern matching on instances of std::optional without having to define the is_optional trait and its partial specialization?
Maybe using implicit deduction guides for std::optional?
I mean... something as
using S = decltype(std::optional{std::declval<R>()});
static_assert( std::is_same_v<R, S>, "R isn't a std::optional" );
Explanation.
When R is std::optional<T> for some T type, std::optional{r} (for an r value of type R) should call the copy constructor and the resulting value should be of the same type R.
Otherwise, the type should be different (std::optional<R>).
The following is a full compiling example.
#include <iostream>
#include <optional>
template <typename T>
bool isOptional ()
{
using U = decltype(std::optional{std::declval<T>()});
return std::is_same_v<T, U>;
}
int main ()
{
std::cout << isOptional<int>() << std::endl; // print 0
std::cout << isOptional<std::optional<int>>() << std::endl; // print 1
}
Anyway, I support the suggestion by super: create a more generic type-traits that receive std::option as template-template argument.
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.
The following meta-function computes if a given type is a random access iterator:
template <class I>
struct is_random_access
: boost::is_convertible
< typename boost::iterator_traversal<I>::type
, boost::random_access_traversal_tag
>
{};
Now of course, this doesn't work if I is not an iterator at all, as there is no boost::iterator_traversal<I> is defined.
Two independent questions:
How to make is_random_access return false (rather than fail to compile) when I is not an iterator?
Is this a good way to detect if an iterator is random access traverseable?
For your first question you can actually use the SFINEA example from wikipedia:
template <class... Ts> using void_t = void;
template <class I, class = void>
struct is_random_access: boost::false_type
{};
template <class I>
struct is_random_access<I,void_t<typename std::iterator_traits<I>::iterator_category> >
: boost::is_convertible
<typename boost::iterator_traversal<I>::type, boost::random_access_traversal_tag>
{};
If the input type does not have the iterator_category type defined, it will fall back to the default struct, if it is defined, it will use your specialization.
For the second question I am no expert. However, my interpretation agrees with yours: for every iterator with std::random_access_iterator_tag, the boost::iterator_traversal<>::type will be convertible to boost::random_access_traversal_tag.
Update: Fixed the problem that int* is not regcognized as a valid iterator. Replaced I:iteratory_category by std::iterator_traits<I>::iterator_category.
I asked a question that has several references to the code:
template <typename...>
using void_t = void;
I believe I have a generally misunderstand alias templates:
Why wouldn't you just evaluate whatever template parameter you're passing into an alias template in an enable_if_t or conditional_t statement?
Is the code above just about doing an enable_if_t on multiple template parameters at once?
Secondly, I believe that I have a specific misunderstanding of the role of void_t. This comment states that the C++17 standard defines void_t. Here's what I don't get:
Isn't void_t just an arbitrary name? If I still have to define template <typename...> using void_t = void; wherever I plan to use void_t what's the point of standardizing an arbitrary name?
In Barry's example from your linked question:
template<typename T, typename = void>
struct has_to_string
: std::false_type { };
template<typename T>
struct has_to_string<T,
void_t<decltype(std::to_string(std::declval<T>()))>
>
: std::true_type { };
void_t is just used to translate the type deduced by decltype to void so that it matches the default argument to the primary template definition. The SFINAE is all taken care of by the decltype expression. You could just as easily do the following:
//use , void() instead of wrapping in void_t
//this uses the comma operator to check the type of the to_string call, then change the type to void
decltype(std::to_string(std::declval<T>()), void())
The former version is much easier to read and void_t doesn't require decltype to work.
If void_t is available in your implementation you don't need to redefine it. When it's standardised it will be available just like any of the other alias templates in the standard.
Think about it this way: if T is int, which has a valid std::to_string overload, deduction will look like this:
has_to_string<int> -> has_to_string<int,void> because of the default argument. So lets look for specializations of has_to_string with those arguments.
template<typename T>
struct has_to_string<T,
void_t<decltype(std::to_string(std::declval<T>()))>
>
: std::true_type { };
Okay, that is a partial specialization for some T and some dependent type. Let's work out that type:
void_t<decltype(std::to_string(std::declval<T>()))>
//std::to_string(int&&) is valid and returns a std::string
void_t<std::string>
//void_t changes types to void
void
Now our specialization looks like this:
template<>
struct has_to_string<int,void>
: std::true_type { };
This matches our instantiation of has_string<int,void>, so has_to_string<int> inherits from std::true_type.
Now think about it when T is struct Foo{};. Again, let's work out that dependent type:
void_t<decltype(std::to_string(std::declval<T>()))>
//wait, std::to_string(Foo&&) doesn't exist
//discard that specialization
With that specialization discarded, we fall back to the primary template:
template<typename T, typename = void>
struct has_to_string
: std::false_type { };
So has_to_string<Foo> inherits from std::false_type.
I don't think the shown example really shows what void_t is good for as it only shows one use case, but when you look at
template<typename T>
struct has_to_string<T,
void_t<decltype(std::to_string(std::declval<T>()))>
>
: std::true_type { };
it is not so much different from
template<typename T>
struct has_to_string<T,
decltype(std::to_string(std::declval<T>()), void())
>
: std::true_type { };
And for this statement:
The former version is much easier to read and void_t doesn't require decltype to work.
I think the advantage in readability is quite small and the second part makes no sense, when decltype doesn't work, SFINAE kicks in as expected.
One example where void_t is more useful is the one from the proposal:
// primary template handles types that have no nested ::type member
template< class, class = void_t<> >
struct has_type_member
: std::false_type { };
// specialization recognizes types that do have a nested ::type member
template< class T >
struct has_type_member<T, void_t<typename T::type>>
: std::true_type { }
As you can see, even the primary template uses void_t to increase the readability as it now matches the specialization. That is not strictly necessary, but I like it. The real power comes when you think about the alternatives. Without void_t, the specialization is now more complicated:
template< class T >
struct has_type_member<T, decltype(typename T::type, void())>
: std::true_type { }
wouldn't work as T::type names a type, not an expression. You therefore need
template< class T >
struct has_type_member<T, decltype(std::declval<typename T::type>(), void())>
: std::true_type { }
The whole expression becomes longer, more tricky and it might suffer from edge-cases you forgot to handle. This is where void_t really helps, the other uses are then just a small improvement and they increase consistency.
I have a constructor like that :
class MyClass
{
template<class TI> MyClass(TI first, TI last);
};
template<class TI> MyClass::MyClass(TI first, TI last)
{
;
}
I would like to enable this constructor only if TI is an iterator (that means TI has an iterator_category I think). How to do that in C++ 2011 using an enable_if as a default template parameter (in the declaration and in the definition) ?
Thank you very much.
It depends on what you want. If there are no other overloads, it can be ok with just nothing at all. The compiler will produce an error if a type is passed that doesn't provide the necessary operation.
If you really want to limit it to iterators, it's preferable to do so with a static_assert, since it produces an error with a nice custom error message, instead of "ambiguous function call, here are all the gazillion overloads I could find: follows endless list of overloads" or "could not find function, find it yourself".
If there is another templated overload that conflicts, then you do indeed need some enable_if thing. I wrote a blog post about using enable_if with C++11 features, and why default template parameters are not very good for that. I settled with something like this instead:
enum class enabler {};
template <typename Condition>
using EnableIf = typename std::enable_if<Condition::value, enabler>::type;
class MyClass
{
template<class TI, EnableIf<is_iterator<TI>>...> MyClass(TI first, TI last);
};
template<class TI, EnableIf<is_iterator<TI>>...> MyClass::MyClass(TI first, TI last)
{ /* blah */ }
All that you need now is a trait for the test. I think testing for the existence of iterator_category is enough, but it should be done with std::iterator_traits, because pointers are iterators and don't have nested typedefs.
That can be done with the usual techniques that use SFINAE. With C++11, I do the following:
template <typename T>
struct sfinae_true : std::true_type {};
struct is_iterator_tester {
template <typename T>
static sfinae_true<typename std::iterator_traits<T>::iterator_category> test(int);
template <typename>
static std::false_type test(...);
};
template <typename T>
struct is_iterator : decltype(is_iterator_tester::test<T>(0)) {};
All that said, this could have been done with the traditional technique of using a defaulted function parameter:
class MyClass
{
template<class TI>
MyClass(TI first, TI last,
typename std::iterator_traits<T>::iterator_category* = nullptr)
};
template<class TI>
MyClass::MyClass(TI first, TI last,
typename std::iterator_traits<T>::iterator_category*)
{ /* blah */ }