I would like to understand how the assignment of int & to double below in conjunction with declval works? Is T deduced to something other than int & ?
#include <iostream>
#include <type_traits>
#include <utility>
template <typename T, typename U, typename = void>
struct assignment : std::false_type{};
template <typename T, typename U>
struct assignment<T, U, std::void_t<decltype(std::declval<T>() = std::declval<U>())>> : std::true_type {};
int main() {
// int &x = 23.4; // does not compile since x is not a const ref
static_assert(assignment<int &, double>::value); // why is this OK?
}
Is T deduced to something other than int & ?
T will still be deduced as int&, so std::declval<T>() = std::declval<U>() will be roughly equivalent to
int& f();
double&& g();
f() = g(); // assignment
Note that f() = g() is still well-formed because f() returns a reference to an already created int that can be assigned to a double. But if you do int& f() { return 23.4; }, then you will get a compile error because non-const lvalue references cannot be initialized by a rvalue. Instead, you can create a helper function which take T as its parameter type
template <typename T, typename U, typename = void>
struct initialize : std::false_type{};
template<typename T>
void accepted(T);
template <typename T, typename U>
struct initialize<T, U,
std::void_t<decltype(accepted<T>(std::declval<U>()))>> : std::true_type {};
This will make static_assert(initialize<int &, double>::value) fail because int& cannot be rvalue-initialized.
Related
I have some variant using V = std::variant<A, B, C> and some function foo with the prototype void foo(const T&).
And want my function foo to be std::enable_ifed if one of V's types are passed (without indicating them explicitly).
My V will get more and more types in time, because of that, solution like
template<class T,
typename std::enable_if<
std::is_same_v<T, A> || std::is_same_v<T, B> || std::is_same_v<T, C>,
int>::type = 0>
void foo(const T&);
is not acceptable.
Here is a boost solution.
Is it possible to implement the logic for std::variant?
Ideally, the type trait should look like is_one_of_variants_types<V, T>.
template <typename, typename>
constexpr bool is_one_of_variants_types = false;
template <typename... Ts, typename T>
constexpr bool is_one_of_variants_types<std::variant<Ts...>, T>
= (std::is_same_v<T, Ts> || ...);
template <typename T>
auto foo(const T&)
-> std::enable_if_t<is_one_of_variants_types<V, T>>;
DEMO
And want my function foo to be std::enable_if ed if one of V's types are passed (without indicating them explicitly).
I suppose you can simply try, inside a decltype(), to emplace() a T value inside a V value.
I mean... something as follows
#include <variant>
#include <type_traits>
struct A {};
struct B {};
struct C {};
using V = std::variant<A, B, C>;
template <typename T>
auto foo (T const & t)
-> std::void_t<decltype( std::declval<V>().emplace<T>(t) )>
{ }
int main ()
{
foo(A{}); // compile
// foo(0); // compilation error
}
Obviously this works only if all variant types are different and with a copy constructor (implicit or explicit).
Is there a way to obtain the return type of a template function/generic lambda without having to specify the argument types?
template<class F>
struct unique_result_of {
using type = std::result_of_t<F( *any valid parameter combination that F can be called upon* )>;
}
auto f = [](auto x, auto y, int z) -> int {};
auto g = [](double x, auto y) -> float {};
using return_type_f = typename unique_result_of<decltype(f)>::type; // should be int
using return_type_g = typename unique_result_of<decltype(g)>::type; // should be float
Of couse this should only work if all functions generated by the same template function name do have a unique return type.
Your given example will work with the following, but there are some caveats, mentioned below
#include<type_traits>
#include<utility>
struct ubiq
{
template<typename T>
constexpr operator T&() const;
};
template<size_t>
ubiq make_ubiq();
struct broken_t;
template<typename T>
static constexpr bool is_broken_v = std::is_same_v<T, broken_t>;
template<typename T, size_t I0, size_t... I>
auto call(std::index_sequence<I0, I...>)
-> decltype(std::declval<T>()(make_ubiq<I0>(), make_ubiq<I>()...));
template<typename T>
auto call(std::index_sequence<>) -> decltype(std::declval<T>()());
template<typename T, size_t... I>
auto call(std::index_sequence<I...>) -> broken_t;
template<typename T, size_t N>
using call_t = decltype(call<T>(std::make_index_sequence<N>{}));
template<typename Void, typename...>
struct collapse
{
using type = broken_t;
};
template<typename T>
struct collapse<std::enable_if_t<!is_broken_v<T>>, T>
{
using type = T;
};
template<typename T, typename... Ts>
struct collapse<std::enable_if_t<!is_broken_v<T>>, T, broken_t, Ts...> :
collapse<void, T, Ts...> {};
template<typename... Ts>
struct collapse<void, broken_t, Ts...> :
collapse<void, Ts...> {};
template<typename T, typename... Ts>
struct collapse<std::enable_if_t<!is_broken_v<T>>, T, T, Ts...> :
collapse<void, T, Ts...> {};
template<typename... Ts>
using collapse_t = typename collapse<void, Ts...>::type;
template<typename, typename>
struct unique_call;
template<typename T, size_t... Ns>
struct unique_call<T, std::index_sequence<Ns...>>
{
using type = collapse_t<call_t<T, Ns>...>;
};
template<typename T, size_t N = 10>
using unique_call_t = typename unique_call<T, std::make_index_sequence<N>>::type;
The following asserts passes
auto f = [](auto x, auto y, int z) -> int {return 42;};
auto g = [](double x, auto y) -> float {return 4.2;};
static_assert(std::is_same_v<int, unique_call_t<decltype(f)>>);
static_assert(std::is_same_v<float, unique_call_t<decltype(g)>>);
Live
The way this works is by "scanning" a type and seeing if any number of arguments can be used to call it. The upper limit of arguments has to be pre-specified, but realistically if someone gives me something with more than ten parameters, I'm just gonna pretend that it doesn't exist anyways :D
The set of possible return types are then checked, if there are different types in there, or if there is none, the resulting type will be broken_t.
struct S
{
int operator()(int);
float operator()(float);
};
struct U {};
static_assert(std::is_same_v<broken_t, unique_call_t<S>>); // passes
static_assert(std::is_same_v<broken_t, unique_call_t<U>>); // passes
Caveats
This method cannot differentiate non-existent operator() and one that is overloaded for the same argument count. The following type will be perceived as only having int operator()().
struct S
{
int operator()();
int operator()(int);
float operator()(float);
};
static_assert(std::is_same_v<int, unique_call_t<S>>); // passes!??
I have yet to think of a method that can do this.
Another problem is with templates
template<typename T, std::enable_if_t<std::is_integral<T>>* = nullptr>
int operator()(T);
Since we cheated and created the type ubiq and used that as a placeholder for arguments, it won't play nice with templates, T won't be an integral in this case.
Unfortunately, there is no way of doing this at the moment, as there is no way of asking the compiler what the developer put after -> in a trailing return type. Maybe reflection could make this possible in the future.
Your best bet is either specifying the argument types or using a type that's implicitly convertible to anything else to simulate an invocation of the lambda:
struct any_type
{
template <typename T>
constexpr operator T() { return *this; }
};
This is not perfect as it doesn't work if the lambda's body tries to invoke a method that any_type doesn't "mock".
This question already has answers here:
How is possible to deduce function argument type in C++?
(4 answers)
Closed 1 year ago.
Is it possible to deduce the type of a function parameter? For example, if I have:
void foo(int a);
I would like to deduce the type int as the type of foo's first parameter. A possible use could be:
foo( static_cast< decltype(/* ??? foo's first param ??? */) >(value) );
In this related question, the answers exploit having a member with the same type for deduction, so it does not directly deduce the function parameter type.
Is it possible to deduce the type of a function parameter?
Sure.
With a type traits, by example (argType)
template <typename>
struct argType;
template <typename R, typename A>
struct argType<R(A)>
{ using type = A; };
void foo(int a)
{ }
int main()
{
long value = 1L;
foo( static_cast<typename argType<decltype(foo)>::type>(value) );
}
If you're interrested in a little more generic solution, the following example show how create and use a type traits to detect the return type or the n-th argument type
#include <string>
template <std::size_t N, typename T0, typename ... Ts>
struct typeN
{ using type = typename typeN<N-1U, Ts...>::type; };
template <typename T0, typename ... Ts>
struct typeN<0U, T0, Ts...>
{ using type = T0; };
template <std::size_t, typename>
struct argN;
template <std::size_t N, typename R, typename ... As>
struct argN<N, R(As...)>
{ using type = typename typeN<N, As...>::type; };
template <typename>
struct returnType;
template <typename R, typename ... As>
struct returnType<R(As...)>
{ using type = R; };
long bar (int a, std::string const &)
{ return a; }
int main()
{
long valI = 1L;
char const * valS = "abc";
bar( static_cast<typename argN<0U, decltype(bar)>::type>(valI),
static_cast<typename argN<1U, decltype(bar)>::type>(valS) );
static_assert(
std::is_same<long,
typename returnType<decltype(bar)>::type>::value, "!");
}
A slightly generalized version of the answer by #max66:
template <typename> struct FirstArgument;
template <typename R, typename A, typename... Args>
struct FirstArgument<R(A, Args...)>
{
using type = A;
};
template <typename T>
using first_agument_t = typename FirstArgument<T>::type;
void foo(int a){ }
void bar(int a, double b){ }
int main()
{
long value = 1L;
foo(static_cast<first_agument_t<decltype(foo)>>(value) );
bar(static_cast<first_agument_t<decltype(bar)>>(value), 0);
}
Below SFINAE code with variadic templates compiles nicely using clang 3.7.1, C++14:
#include <array>
#include <iostream>
#include <vector>
#include <cstdint>
enum class Bar : uint8_t {
ay, bee, see
};
struct S {
static void foo() {}
// std::begin(h) is defined for h of type H
template<typename H, typename... T>
static typename std::enable_if<std::is_pointer<decltype(std::begin(std::declval<H>()))*>::value>::type
foo(const H&, T&&... t)
{ std::cout << "container\n"; foo(std::forward<T>(t)...); }
// H is integral
template<typename H, typename... T>
static typename std::enable_if<std::is_integral<typename std::remove_reference<H>::type>::value>::type
foo(const H&, T&&... t)
{ std::cout << "integer\n"; foo(std::forward<T>(t)...); }
// H is an enum with underlying type = uint8_t
/*
template<typename H, typename... T>
static typename std::enable_if<std::is_same<typename std::underlying_type<H>::type,uint8_t>::value>::type
foo(const H&, T&&... t)
{ std::cout << "enum\n"; foo(std::forward<T>(t)...); }
*/
};
int main()
{
S::foo(std::array<int,8>(), 5, 5L, std::vector<int>{}, 5L);
}
I want the correct overload of foo to be called recursively, based on the type H:
if std::begin(h) is defined for an h of type H, I want the
overload number 1 to be chosen
if H is an "integral type", I want overload number 2.
This works as it is. But if I add another overload for enum types (you can try to un-comment the third overload), then I get:
error: only enumeration types have underlying types
I agree that only enums got an underlying type, hence why is Not the third overload (with std::underlying_type) get SFINAE-d away?
std::underlying_type is not SFINAE friendly. Attempting to access std::underlying_type<T>::type for a non-enumeration type results in undefined behavior (often a hard error), not substitution failure.
You need to ascertain that the type at issue is an enumeration type first, before attempting to access its underlying type. Writing this in line would be something along the lines of typename std::enable_if<std::is_enum<H>::value, std::underlying_type<H>>::type::type. Replacing the typename std::underlying_type<H>::type in your return type with this hideous mess and you get an even more hideous mess that works :)
If you find yourself needing to do this often - or just don't want to write typename std::enable_if<std::is_same<typename std::enable_if<std::is_enum<H>::value, std::underlying_type<H>>::type::type, uint8_t>::value>::type - you can write a SFINAE-friendly underlying_type:
template<class T, bool = std::is_enum<T>::value>
struct safe_underlying_type : std::underlying_type<T> {};
template<class T>
struct safe_underlying_type<T, false /* is_enum */> {};
Here's a solution inspired from T.C.'s solution that worked for my use case:
template <typename T, bool = std::is_enum<T>::value>
struct relaxed_underlying_type {
using type = typename std::underlying_type<T>::type;
};
template <typename T>
struct relaxed_underlying_type<T, false> {
using type = T;
};
Example Usage:
template <typename T>
struct UnwrapEnum {
using type =
typename std::conditional<
std::is_enum<T>::value,
typename relaxed_underlying_type<T>::type,
T>
::type;
};
enum class MyEnum : int {};
class MyClass {};
int main() {
UnwrapEnum<MyEnum>::type x;
static_assert(std::is_same<decltype(x), int>::value);
UnwrapEnum<MyClass>::type y;
static_assert(std::is_same<decltype(y), MyClass>::value);
return 0;
}
Why reference collapsing does not apply in
template<typename T, template<typename> class C>
void f(C<T> && x); // x declaration is an rvalue!
How can I perfect forward and how can I avoid overloading all combinations of const lvalue ref, lvalue ref, rvalue ref in
template<typename T> // not necessary a template template class here
void f(C<T>, C<T>, C<T>, ..., C<T>)
{
// do something with T
// move or copy arguments to a function
}
You will ned to use some kind of SFINAE, unfortunately
template<typename T>
struct HasOneTypeParam : std::false_type { };
template<typename T, template<typename> class C>
struct HasOneTypeParam<C<T>> : std::true_type { };
template<typename ...T>
struct SlurpThemAll { typedef int type; };
template<typename ...T,
typename SlurpThemAll<
bool[HasOneTypeParam<typename std::decay<T>::type>::value * 2 - 1]...
>::type = 0>
void f(T &&... x);
Depending on what you actually want to do, you can require all of the std::decay<T>::type to be the same type with some more SFINAE hacks.