In this code, I'm trying to generalize Test from using Arg to using Args.... The problem is the default template argument. What I have below compiles, except when I uncomment the commented-out line in main():
#include <iostream>
#include <type_traits>
struct A {
void foo(int) const {}
void foo(int, bool, char) const {}
};
template <typename...> struct voider { using type = void; };
template <typename... Ts>
using void_t = typename voider<Ts...>::type;
template <typename T, typename Arg, typename = void_t<T>>
struct Test : std::false_type {};
template <typename T, typename Arg>
struct Test<T, Arg, void_t<decltype(std::declval<T&>().foo(std::declval<Arg>()))>> :
std::true_type {};
// Trying to generalize Test with Args... instead of Arg
template <typename T, typename, typename... Args> struct Check;
template <typename T, typename... Args>
struct Check<T, void_t<T>, Args...> : std::false_type {};
template <typename T, typename... Args>
struct Check<T, void_t<decltype(std::declval<T&>().foo(std::declval<Args>()...))>, Args...>
: std::true_type {};
template <typename T, typename... Args>
using CheckArgs = Check<T, void_t<T>, Args...>;
int main() {
std::cout << std::boolalpha << Test<A, int>::value << '\n'; // true
// std::cout << CheckArgs<A, int, bool, char>::value << '\n'; // ambiguous
}
The last line in main() is ambiguous. Firstly, why is it ambiguous, while the first line in main() is not? And secondly, how to fix the code so that the last line in main will compile (it is supposed to evaluate to true since int, bool, char are arguments of A::foo)?
You want
template <typename T, typename, typename... Args>
struct Check : std::false_type {};
template <typename T, typename... Args>
struct Check<T, void_t<decltype(std::declval<T&>().foo(std::declval<Args>()...))>, Args...>
: std::true_type {};
You want the primary template to provide the default case - which is false, and the partial specialization to supply the true case. When you write two partial specializations, both are viable, and there's no ordering between the two, so it ends up being ambiguous
And this is just reimplementing a more constrained version of std::experimental::is_detected.
Related
In c++ i want to get the type of the arguments of a function.
The issue is i don't want to get the type for all of the arguments only the ones after the first one
template <typename T>
struct FuncTraits : FuncTraits<decltype(&T::operator())> {};
template <typename C, typename R, typename... Args>
struct FuncTraits<R(C::*)(Args...) const> : FuncTraits<void(*)(Args...)> {};
template <typename... Args> struct FuncTraits<void(*)(Args...)> {
using ArgCount = std::integral_constant<std::size_t, sizeof...(Args)>;
using ArgsType = std::tuple<typename std::decay<Args>::type...>;
};
In this example it gets the type for all of the arguments, but i want something more like this
template <typename T>
struct FuncTraits : FuncTraits<decltype(&T::operator())> {};
template <typename C, typename R, typename... Args>
struct FuncTraits<R(C::*)(int, Args...) const> : FuncTraits<void(*)(int unused, Args...)> {};
template <typename... Args> struct FuncTraits<void(*)(int unused, Args...)> {
using ArgCount = std::integral_constant<std::size_t, sizeof...(Args)>;
using ArgsType = std::tuple<typename std::decay<Args>::type...>;
};
Yet this fails complete to compile.
How do i achieve something like this?
You can add another template type to the base overload of FuncTraits:
#include <iostream>
#include <typeinfo>
struct foo
{
float operator ()(int a, char b, unsigned int c) const { return 0; }
};
template <typename T>
struct FuncTraits : FuncTraits<decltype(&T::operator())> {};
template <typename C, typename R, typename... Args>
struct FuncTraits<R(C::*)(Args...) const> : FuncTraits<void(*)(Args...)> {};
template <typename Arg, typename... Args> struct FuncTraits<void(*)(Arg, Args...)> {
using ArgCount = std::integral_constant<std::size_t, sizeof...(Args)>;
using ArgsType = std::tuple<typename std::decay<Args>::type...>;
};
int main()
{
using FooTraits = FuncTraits<foo>;
std::cout << FooTraits::ArgCount() << '\n'; \\ prints '2'
std::cout << typeid(FooTraits::ArgsType).name() << '\n'; \\ prints 'class std::tuple<char,unsigned int>'
}
The following code complains on the line with dp1 "term does not evaluate to a function taking 0 arguments". I expect that dp1 would match the IsInstantiation specialization of Dispatch, but it errors.
How would I change this code so that it will work as I expect? I am using VS2015 and cannot use std::invoke or std::is_invokable.
#include <type_traits>
template <typename>
class TemplateA {};
struct Callable
{
void operator()() {};
};
template <typename F, typename... Args>
constexpr auto Invoke(F &&f, Args &&... args)
{
return f(std::forward(args)...);
}
template <template<typename...> class TT, typename T>
struct IsInstantiation : std::false_type {};
template <template<typename...> class TT, typename... Ts>
struct IsInstantiation<TT, TT<Ts...>> : std::true_type {};
template <typename, typename = void>
struct HasNoArgs : std::false_type {};
template <typename T>
struct HasNoArgs<T, std::void_t<decltype(Invoke(T{}))>> : std::true_type {};
template <typename T1, typename = void>
class Dispatch;
template <typename T1>
class Dispatch<T1, std::enable_if_t<HasNoArgs<T1>::value>> {};
template <typename T1>
class Dispatch<T1, std::enable_if_t<IsInstantiation<TemplateA, T1>::value>> {};
int main(int argc, char *argv[])
{
Dispatch<TemplateA<int>> dp1;
Dispatch<Callable> dp2;
return 0;
}
There are several problems with your code.
This is an improper use of forward:
return f(std::forward(args)...);
The function parameter for std::forward is a non-deduced context, on purpose. So this call will never succeed. You want f(std::forward<Args>(args)...)
Invoke is SFINAE unfriendly:
template <typename F, typename... Args>
constexpr auto Invoke(F &&f, Args &&... args)
{ ... }
If F is indeed invocable with Args..., this function will work fine. If it isn't, then this is a hard compile error because the problem only surfaces in the body, which is outside of the "immediate context" of the substitution. As a result, it's not a "substitution failure" (the SF in SFINAE), it's an instantiation failure. In HasNoArgs, you're trying to see if you can invoke it - which means you need a substitution failure. To do that, you need to lift the expression into the immediate context:
template <typename F, typename... Args>
constexpr auto Invoke(F &&f, Args &&... args)
-> decltype(f(std::forward<Args>(args)...))
{ return f(std::forward<Args>(args)...); }
Now, it's SFINAE friendly (and additionally, can return a reference).
Your HasNoArgs checks more than you think it does:
template <typename T>
struct HasNoArgs<T, std::void_t<decltype(Invoke(T{}))>> : std::true_type {};
This requires that T can be invoked with no arguments, which after fixing #1 and #2 above, is now possible. But it also requires that T{} be a valid expression. Non-default-constructible callables will fail this test, but not for the reason that the test claims it's looking for. To fix that, use std::declval:
template <typename T>
struct HasNoArgs<T, std::void_t<decltype(Invoke(std::declval<T>()))>> : std::true_type {};
I have the following code:
template <template <class...> class Temp, class Specialization>
struct IsSpecialization : std::false_type {};
template <template <typename...> class Temp1,
template <typename...> class Temp2, typename... Ts>
struct IsSpecialization<Temp1, Temp2<Ts...>>
: std::is_same<Temp1<Ts...>, Temp2<Ts...>> {};
struct ExprKindMerge {};
struct ExprKindSequence {};
template <class Tag, class... Args>
struct Expr {
std::tuple<Args...> tup;
constexpr std::tuple<Args...> toStdTuple() const {
return this->tup;
}
constexpr std::size_t size() const noexcept {
return std::tuple_size<decltype(tup)>{};
}
};
template <class...Args>
using MergeExpr = Expr<ExprKindMerge, Args...>;
template <class...Args>
using SequenceExpr = Expr<ExprKindSequence, Args...>;
And this function, which sometimes receives a SequenceExpr<Something> as the template parameter:
template <class FullExpr>
auto f(FullExpr expr) {
///**************THIS RETURNS FALSE I EXPECT TRUE
///Type of full expr is Expr<ExprKindSequence, ...> which is
/// the expansion of SequenceExpr<...>
if constexpr (IsSpecialization<SequenceExpr, FullExpr>::value)
//...
}
I want to be able to detect if FullExpr is a specialization of SequenceExpr but it fails for some unknown reason.
Why it fails is easy. For a SequenceExpr<ExtraArgs...>, Temp2 is deduced to be Expr and Ts... is deduced to be ExprKindSequence, ExtraArgs.... Then Temp1<Ts...> is Expr<ExprKindSequence, ExprKindSequence, ExtraArgs...> and is obviously not the same as Temp2<Ts...>.
I know of no fully generic way to do this. After all, such a hypothetical template would presumably need to return true for IsSpecialization<std::remove_reference_t, int>...
If we limit it to alias templates that use their parameters in deducible contexts (which is the case in your example), then one possible way is to ask the question "Can I deduce the Ts... in Temp<Ts...> from Specialization?":
namespace detail {
template<class> class type {};
template<template<class...> class Temp, class...Ts>
void try_deduce(type<Temp<Ts...>>);
}
template <template <class...> class, class, class = void>
struct IsSpecialization : std::false_type {};
template <template <typename...> class Temp, class Specialization>
struct IsSpecialization<Temp, Specialization,
decltype(detail::try_deduce<Temp>(detail::type<Specialization>()))>
: std::true_type {};
static_assert(IsSpecialization<SequenceExpr, SequenceExpr<int>>()());
static_assert(IsSpecialization<Expr, SequenceExpr<int>>()());
static_assert(!IsSpecialization<MergeExpr, SequenceExpr<int>>()());
static_assert(!IsSpecialization<SequenceExpr, MergeExpr<int>>()());
If your interested only in detecting specializations of SequenceExpr, the best I can imagine is to add a specialization of IsSpecialization defined as follows
template <typename... Ts>
struct IsSpecialization<SequenceExpr, Expr<ExprKindSequence, Ts...>>
: std::true_type
{ };
Obviously this isn't a general solution that I don't think it's possible.
Take in count that if you call f with a (by example) Expr<ExprKindSequence, int, long> value
f(Expr<ExprKindSequence, int, long>{}); // result true !!!
the specialization of IsSpecialization above match and you get true; I don't know if is what do you want.
The following is a full working example
#include <tuple>
#include <iostream>
#include <type_traits>
template <template <typename...> typename Temp, typename Specialization>
struct IsSpecialization : std::false_type
{ };
template <template <typename...> class Temp1,
template <typename...> class Temp2, typename... Ts>
struct IsSpecialization<Temp1, Temp2<Ts...>>
: std::is_same<Temp1<Ts...>, Temp2<Ts...>>
{ };
struct ExprKindMerge {};
struct ExprKindSequence {};
template <typename Tag, typename... Args>
struct Expr
{
std::tuple<Args...> tup;
constexpr std::tuple<Args...> toStdTuple () const
{ return this->tup; }
constexpr std::size_t size () const noexcept
{ return std::tuple_size<decltype(tup)>{}; }
};
template <typename ... Args>
using MergeExpr = Expr<ExprKindMerge, Args...>;
template <typename ... Args>
using SequenceExpr = Expr<ExprKindSequence, Args...>;
template <typename ... Ts>
struct IsSpecialization<SequenceExpr, Expr<ExprKindSequence, Ts...>>
: std::true_type
{ };
template <class FE>
auto f (FE expr)
{ std::cout << IsSpecialization<SequenceExpr, FE>::value << std::endl; }
int main ()
{
f(SequenceExpr<int, long>{}); // print 1
f(Expr<ExprKindSequence, int, long>{}); // print 1 (?)
f(Expr<int, long>{}); // print 0
f(int{}); // print 0
}
Consider the following code:
template <class F, class... Args, class = std::void_t<>>
struct is_invokable
: std::false_type {};
template <class F, class... Args>
struct is_invokable<F, Args..., std::void_t<std::invoke_result_t<F, Args...>>>
: std::true_type {};
The goal is to have a trait that is able to tell whether a callable of type F is invokable with arguments of type Args....
However, it fails to compile because:
error: parameter pack 'Args' must be at the end of the template parameter list
What is the (elegant) way to do this in C++17?
namespace details {
template <class F, class, class... Args>
struct is_invokable : std::false_type {};
template <class F, class... Args>
struct is_invokable<F, std::void_t<std::invoke_result_t<F, Args...>>, Args...>
: std::true_type {};
}
template <class F, class... Args>
using is_invokable=typename ::details::is_invokable<F, void, Args...>::type;
I propose an helper struct (is_invokable_h) and the use of std::tuple to wrap the Args...
Something like
#include <type_traits>
#include <utility>
template <typename, typename, typename = void>
struct is_invokable_h : std::false_type
{};
template <typename F, typename ... Args>
struct is_invokable_h<F, std::tuple<Args...>,
std::void_t<std::invoke_result_t<F, Args...>>>
: std::true_type
{};
template <typename F, typename ... Args>
struct is_invokable : is_invokable_h<F, std::tuple<Args...>>
{};
int foo (int)
{ return 0; }
int main()
{
static_assert( true == is_invokable<decltype(foo), int>{} );
static_assert( false == is_invokable<decltype(foo), int, int>{} );
}
template <unsigned int N> class myclass
{
public:
template <typename... Args> void mymethod(Args... args)
{
// Do interesting stuff
}
};
I want mymethod to be called only with exactly N doubles. Is that possible? That is, say that I have:
myclass <3> x;
x.mymethod(3., 4., 5.); // This works
x.mymethod('q', 1., 7.); // This doesn't work
x.mymethod(1., 2.); // This doesn't work
How can I get this done?
For the number of arguments constraint you can easily check if sizeof...(Args) == N but for checking if all the arguments are doubles you need to build a recursive type trait that checks std::is_same for each of the arguments.
template<typename...>
struct are_same : std::true_type
{};
template<typename T>
struct are_same<T> : std::true_type
{};
template<typename T, typename U, typename... Types>
struct are_same<T, U, Types...> :
std::integral_constant<bool, (std::is_same<T, U>::value && are_same<T, Types...>::value)>
{};
Notice are_same is first declared and then specialized.
Then just implement the constraint in your method return type using std::enable_if by taking advantage of SFINAE.
template <unsigned int N> class myclass
{
public:
template <typename... Args>
typename std::enable_if<(are_same<double, Args...>::value && sizeof...(Args) == N), void>::type
/* void */ mymethod(Args... args)
{
// Do interesting stuff
}
};
Can try something like following :
#include <type_traits>
template<class T, class...>
struct all_same : std::true_type
{};
template<class T, class U, class... TT>
struct all_same<T, U, TT...>
: std::integral_constant<bool, std::is_same<T,U>{} && all_same<T, TT...>{}>
{};
template <unsigned int N> class myclass
{
public:
template <typename... Args>
typename std::enable_if<sizeof...(Args) == N, void >::type mymethod(Args... args)
{
static_assert(all_same<double, Args...>{},
"Not all args as Double");
}
};
<Demo>