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 cannot give default arguments to the same template parameters in different declarations in the same scope. The compiler will not allow the following example: template<class T = char> class X; template<class T = char> class X { };
Just like in case of the function arguments, template parameters can have their default values. All template parameters with a default value have to be declared at the end of the template parameter list.
Templates can be template parameters. In this case, they are called template parameters. The container adaptors std::stack, std::queue, and std::priority_queue use per default a std::deque to hold their arguments, but you can use a different container. Their usage is straightforward.
In C++ this can be achieved using template parameters. A template parameter is a special kind of parameter that can be used to pass a type as argument: just like regular function parameters can be used to pass values to a function, template parameters allow to pass also types to a function.
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
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With