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 {};
Related
(This question has been significantly edited, sorry.)
Suppose I have several non-constexpr function templates, which default to being deleted:
template <typename T> void foo() = delete;
template <typename T> int bar(int x) = delete;
// etc.
and have some explicit specialization as an exception to the general-case deletion.
I want to write code (e.g. a trait class?) which, given the identifier of one of these functions and a type T, detects, at compile-time, whether the specified function is explicitly specialized for type T. The code needs to be generic, i.e. not a separate detector for each of the functions.
Notes:
Looking for a C++11 solution.
We may assume the specified function is deleted by default - if that helps.
Ideally, it would like like instantiation_exists<decltype(foo), foo, int>::value or instantiation_exists<int>(foo, tag<int>) or instantiation_exists(foo, tag<int>) or something along those lines.
Edit: #Jarod42's wrote up an SFINAE example in a comment on an earlier version of this question, which was about a per-single-function detector. I tried to generalize/genericize it using a template-template parameter:
#include <type_traits>
template <typename T> void foo() = delete;
template <> void foo<int>() {}
template <template<typename U> typename F, typename T, typename = decltype(F<T>()) >
std::true_type test(int);
template <template<typename U> typename F, typename T>
std::false_type test(...);
template <typename T>
using foo_is_defined = decltype(test<foo<T>, T>(0));
static_assert(foo_is_defined<int>::value);
static_assert(not foo_is_defined<int*>::value);
but that was a wash (Coliru).
We cannot pass template function, or overloads in template parameter.
We can turn those function in functor:
template <typename T>
struct identity_type
{
using type = T;
};
template <typename F, typename T, typename = decltype(std::declval<F>()(identity_type<T>{})) >
std::true_type test(int);
template <typename F, typename T>
std::false_type test(...);
auto foos = [](auto tag, auto&&... args)
-> decltype(foo<typename decltype(tag)::type>((decltype(args))(args)...))
{
return foo<typename decltype(tag)::type>((decltype(args))(args)...);
};
template <typename T>
using is_foo = decltype(test<decltype(foos), T>(0));
Demo
I use generic lambda, so C++14.
in C++11, it would be really verbose:
struct foos
{
template <typename T, typename... Ts>
auto operator()(identity_type<T>, Ts&&... args) const
-> decltype(foo<T>(std::forward<Ts>(args)...))
{
return foo<T>(std::forward<Ts>(args)...);
};
};
I am trying to modify the is_detected idiom to allow passing variadic arguments to it. I need this since some of my detected member functions will have user provided arguments.
So far, this is what I got working. You give the extra args to is_detected_args_v, and in theory, the template specialization would kick in and compile correctly. Thus giving std::true_type.
#include <type_traits>
#include <cstdio>
// slightly modified (and simplified) is_detected
template <template <class, class...> class Op, class T, class = void, class...>
struct is_detected_args : std::false_type {};
template <template <class, class...> class Op, class T, class... Args>
struct is_detected_args<Op, T, std::void_t<Op<T, Args...>>, Args...>
: std::true_type {};
template <template <class, class...> class Op, class T, class... Args>
inline constexpr bool is_detected_args_v
= is_detected_args<Op, T, Args...>::value;
// has_func, checks the function starts with int, and then Args&...
template <class T, class... Args>
using has_func = decltype(std::declval<T>().func(
std::declval<int>(), std::declval<Args&>()...));
// has the func
struct obj {
void func(int, double&, double&) {
printf("potato\n");
}
};
int main(int, char**) {
obj o;
if constexpr(is_detected_args_v<has_func, obj, double, double>) {
double d = 0;
double d2 = 42;
o.func(42, d, d2);
}
}
You can run the example here (tested on all 3 compilers) : https://wandbox.org/permlink/ttCmWSVl1XVZjty7
The problem is, the specialization is never chosen and the conditional is always false. My question is two-folds.
Is this even possible?
Why doesn't is_detected get specialized?
Thx
The main issue here is misunderstanding what void_t does. As a refresher, see how does void_t work?. The key idea is that the primary template has a void parameter and the specialization has some complex thing that you want to check wrapped in void_t so that it matches the primary template's parameter. That isn't happening in your example.
We can fix it in two easy steps. First, you have this type T along with Args... There isn't actually any reason to split this up, and it's easier to look at if we don't have extraneous parameters. So here's your attempt just reduced (I also gave a name to the parameter which is supposed to be void):
template <template <class...> class Op, class AlwaysVoid, class...>
struct is_detected_args : std::false_type {};
template <template <class...> class Op, class... Args>
struct is_detected_args<Op, std::void_t<Op<Args...>>, Args...>
: std::true_type {};
template <template <class...> class Op, class... Args>
inline constexpr bool is_detected_args_v = is_detected_args<Op, Args...>::value;
Now it should be easier to see what's missing: the void parameter! You're not passing in a void and you need to. That's an easy fix though:
template <template <class...> class Op, class... Args>
inline constexpr bool is_detected_args_v = is_detected_args<Op, void, Args...>::value;
// ~~~~~
And now it works as expected.
Cppreference also provides a complete implementation of is_detected if you want to look at that too.
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.
Background : I've created the following class C, whose constructor should take N variables of type B& :
class A;
class B
{
A* getA();
};
template<size_t N>
class C
{
public:
template<typename... Args>
inline C(Args&... args) :
member{args.getA()...}
{}
private:
std::array<A*, N> member;
};
Problem : my problem is how to constraint the variadic Args to be all of type B ?
My partial solution : I wanted to define a predicate like :
template <typename T, size_t N, typename... Args>
struct is_range_of :
std::true_type // if Args is N copies of T
std::false_type // otherwise
{};
And redefine my constructor accordingly :
template <typename... Args,
typename = typename std::enable_if<is_range_of_<B, N, Args...>::value>::type
>
inline C(Args&... args);
I've seen a possible solution on this post : https://stackoverflow.com/a/11414631, which defines a generic check_all predicate :
template <template<typename> class Trait, typename... Args>
struct check_all :
std::false_type
{};
template <template<typename> class Trait>
struct check_all<Trait> :
std::true_type
{};
template <template<typename> class Trait, typename T, typename... Args>
struct check_all<Trait, T, Args...> :
std::integral_constant<bool, Trait<T>::value && check_all<Trait, Args...>::value>
{};
So, I could write something like :
template <typename T, size_t N, typename... Args>
struct is_range_of :
std::integral_constant<bool,
sizeof...(Args) == N &&
check_all<Trait, Args...>::value
>
{};
Question 1 : I don't know how to define the Trait, because I need somehow to bind std::is_same with B as first argument. Is there any means of using the generic check_all in my case, or is the current grammar of C++ incompatible ?
Question 2 : My constructor should also accept derived classes of B (through a reference to B), is it a problem for template argument deduction ? I am afraid that if I use a predicate like std::is_base_of, I will get a different instantiation of the constructor for each set of parameters, which could increase compiled code size...
Edit : For example, I have B1 and B2 that inherits from B, I call C<2>(b1, b1) and C<2>(b1, b2) in my code, will it create two instances (of C<2>::C<B1, B1> and C<2>::C<B1, B2>) ? I want only instances of C<2>::C<B, B>.
Define all_true as
template <bool...> struct bool_pack;
template <bool... v>
using all_true = std::is_same<bool_pack<true, v...>, bool_pack<v..., true>>;
And rewrite your constructor to
// Check convertibility to B&; also, use the fact that getA() is non-const
template<typename... Args,
typename = std::enable_if_t<all_true<std::is_convertible<Args&, B&>{}...>>
C(Args&... args) :
member{args.getA()...}
{}
Alternatively, under C++17,
template<typename... Args,
typename = std::enable_if_t<(std::is_convertible_v<Args&, B&> && ...)>>
C(Args&... args) :
member{args.getA()...}
{}
I am afraid that if I use a predicate like std::is_base_of, I will get
a different instantiation of the constructor for each set of
parameters, which could increase compiled code size...
enable_if_t<…> will always yield the type void (with only one template argument given), so this cannot be is_base_ofs fault. However, when Args has different types, i.e. the types of the arguments are distinct, then subsequently different specializations will be instantiated. I would expect a compiler to optimize here though.
If you want the constructor to take precisely N arguments, you can use a somewhat easier method. Define
template <std::size_t, typename T>
using ignore_val = T;
And now partially specialize C as
// Unused primary template
template <size_t N, typename=std::make_index_sequence<N>> class C;
// Partial specialization
template <size_t N, std::size_t... indices>
class C<N, std::index_sequence<indices...>>
{ /* … */ };
The definition of the constructor inside the partial specialization now becomes trivial
C(ignore_val<indices, B&>... args) :
member{args.getA()...}
{}
Also, you do not have to worry about a ton of specializations anymore.
namespace detail {
template <bool...> struct bool_pack;
template <bool... v>
using all_true = std::is_same<bool_pack<true, v...>, bool_pack<v..., true>>;
template<class X> constexpr X implicit_cast(std::enable_if_t<true, X> x) {return x;}
};
The implicit_cast is also in Boost, the bool_pack stolen from Columbo.
// Only callable with static argument-types `B&`, uses SFINAE
template<typename... ARGS, typename = std::enable_if_t<
detail::all_true<std::is_same<B, ARGS>...>>>
C(ARGS&... args) noexcept : member{args.getA()...} {}
Option one, if it's implicitly convertible that's good enough
template<typename... ARGS, typename = std::enable_if_t<
detail::all_true<!std::is_same<
decltype(detail::implicit_cast<B&>(std::declval<ARGS&>())), ARGS&>...>>
C(ARGS&... args) noexcept(noexcept(implicit_cast<B&>(args)...))
: C(implicit_cast<B&>(args)...) {}
Option two, only if they are publicly derived from B and unambiguously convertible:
// Otherwise, convert to base and delegate
template<typename... ARGS, typename = decltype(
detail::implicit_cast<B*>(std::declval<ARGS*>())..., void())>
C(ARGS&... args) noexcept : C(implicit_cast<B&>(args)...) {}
The unnamed ctor-template-argument-type is void in any successful substitution.
Suppose I have a class with the following signature:
template <typename T, typename... Args>
class A;
But how this class behaves should depend on some other parameter, let's say it's the value of T::value:
template <typename T, typename... Args, typename Enable>
class A;
template <typename T, typename... Args, typename = typename std::enable_if<T::value>::type>
class A
{
// do something
};
template <typename T, typename... Args, typename = typename std::enable_if<!T::value>::type>
class A
{
// do something else
};
int main() { return 0; }
However, this program gives the following error:
prog.cpp:6:11: error: parameter pack ‘Args’ must be at the end of the
template parameter list
class A;
I have struggled to find a good source of information on the use of enable_if to select classes with variadic templates. The only question I could find is this one:
How to use std::enable_if with variadic template
But despite the name, this question and its answers aren't much help. If someone could provide or link a guide on how this should be approached and why that would be appreciated.
First of all, what you're trying is writing multiple definitions of a class template. That's not allowed because it violates One definition rule. If you want to do conditional enabling with classes, you need specializations. Also, the compiler error message already told you, you can't have a variadic parameter pack in the middle of a parameter list.
One way to do it would be:
namespace detail {
template<typename T, typename Enable, typename... Args>
class A_impl;
template<typename T, typename... Args>
class A_impl<T, typename std::enable_if<T::value>::type, Args...> {
// code here
};
template<typename T, typename... Args>
class A_impl<T, typename std::enable_if<!T::value>::type, Args...> {
// code here
};
}
template<typename T, typename...Args>
class A : public detail::A_impl<T, void, Args...> {};
Jonathan's way is also perfectly fine if the condition is really a bool, but it might not be useful if you wish to add more specializations that each depend on several conditons.
It looks as though for your purposes you don't need to enable/disable the class, you just need a partial specialization:
template <typename T, bool B = T::value, typename... Args>
class A;
template <typename T, typename... Args>
class A<T, true, Args...>;
template <typename T, typename... Args>
class A<T, false, Args...>;