Universal reference with templated class - c++

Example:
template <typename T>
class Bar
{
public:
void foo(T&& arg)
{
std::forward<T>(arg);
}
};
Bar<int> bar;
bar.foo(10); // works
int a{ 10 };
bar.foo(a); // error C2664: cannot convert argument 1 from 'int' to 'int &&'
It seems that universal references works only with templated functions and only with type deduction, right? So it make no sense to use it with class? And does using of std::forward makes sense in my case?

Note that the preferred terminology (i.e. the one which will be in future versions of the spec) is now forwarding reference.
As you say, a forwarding reference only works with type deduction in a function template. In your case, when you say T&&, T is int. It can't be int& because it has been explicitly stated in your Bar instantiation. As such, reference-collapsing rules can't occur, so you can't do perfect forwarding.
If you want to do perfect forwarding in a member function like that, you need to have a member function template:
template <typename U>
void foo(U&& arg)
{
std::forward<U>(arg); //actually do something here
}
If you absolutely need U to have the same unqualified type as T, you can do a static_assert:
template <typename U>
void foo(U&& arg)
{
static_assert(std::is_same<std::decay_t<U>,std::decay_t<T>>::value,
"U must be the same as T");
std::forward<U>(arg); //actually do something here
}
std::decay might be a bit too aggressive for you as it will decay array types to pointers. If that's not what you want, you could write your own simple trait:
template <typename T>
using remove_cv_ref = std::remove_cv_t<std::remove_reference_t<T>>;
template <typename T, typename U>
using is_equiv = std::is_same<remove_cv_ref<T>, remove_cv_ref<U>>;
If you need a variadic version, we can write an are_equiv trait. First we need a trait to check if all traits in a pack are true. I'll use the bool_pack method:
namespace detail
{
template<bool...> struct bool_pack;
template<bool... bs>
using all_true = std::is_same<bool_pack<bs..., true>, bool_pack<true, bs...>>;
}
template <typename... Ts>
using all_true = detail::all_true<Ts::value...>;
Then we need something to check if each pair of types in Ts... and Us... satisfy is_equiv. We can't take two parameter packs as template arguments, so I'll use std::tuple to separate them (you could use a sentinel node, or split the pack halfway through instead if you wanted):
template <typename TTuple, typename UTuple>
struct are_equiv;
template <typename... Ts, typename... Us>
struct are_equiv <std::tuple<Ts...>, std::tuple<Us...>> : all_true<is_equiv<Ts,Us>...>
{};
Then we can use this like:
static_assert(are_equiv<std::tuple<Ts...>,std::tuple<Us...>>::value,
"Us must be equivalent to Ts");

You're right : "universal references" only appear when the type of a deduced parameter is T&&. In your case, there is no deduction (T is known from the class), hence no universal reference.
In your snippet, std::forward will always perform std::move as arg is a regular rvalue reference.
If you wish to generate a universal reference, you need to make foo a function template :
template <typename T>
class Bar
{
public:
template <typename U>
void foo(U&& arg)
{
std::forward<U>(arg);
}
};

Related

Template parameter pack of mixed templated types (std::function recreation with optional arguments)

I'm attempting to make an std::function alternative that supports optional arguments with defaults. I tried a few different syntactical ideas, but the most realistic seems to be a parameter pack of specialised templates that hold argument data. Here's my desired outer syntax:
Function<
void /*Return type*/,
Arg<false, int> /*Required int parameter*/,
Arg<true, bool, true> /*Optional bool parameter that defaults to true*/
> func;
I would have liked to maintain the Function<ReturnType(args)> syntax but it appears you can only put typenames in parentheses and not classes. Here's my current code:
template<bool Optional, typename Type, Type Default>
class Arg;
template<typename Type, Type Default>
class Arg<false, Type, Default> {};
template<typename Type, Type Default>
class Arg<true, Type, Default> {};
Problem 1: I can't find a way to make the "Default" parameter non-required for the false specialisation. I've tried proxy classes with a default constructor, changing the third argument to a pointer and with specifying nullptr (which isn't really ideal syntactically), and a const reference to a type (which still requires three arguments from the user side) but nothing seems to allow two arguments to be accepted with Function<false, Type>.
Problem 2: I can't find the right syntax to get a parameter pack of mixed template argument types. I've tried (obviously invalid syntax)
template<typename RetType, Arg<bool, typename Type, Type>... args>
class Function{};
and even a double/nested template but I can't make this work either.
All of this indirection really steams from the fact that you can't have multiple parameter packs in a class template so I have to find creative ways to specify optional arguments, but I will take any solution I can get to somehow making this function at compile-time and not having to construct these functions dynamically.
I'm using C++20.
To make the third template argument optional when the first argument is false, you can use a default argument with a std::enable_if:
template <bool Optional, typename T,
T Default = std::enable_if_t<!Optional, T>{}>
class Arg;
This way, Arg<false, int> is equivalent to Arg<false, int, 0>, whereas Arg<true, int> is ill-formed.
You can use generic arguments:
template <typename R, typename... Args>
class Function {
static_assert(std::conjunction_v<is_arg_v<Args>...>);
};
Where is_arg can be something as simple as
template <typename T>
struct is_arg :std::false_type {};
template <bool Optional, typename T, T Default>
struct is_arg<Arg<Optional, T, Default>> :std::true_type {};
template <typename T>
inline constexpr bool is_arg_v = is_arg<T>::value;

Check if class is a template specialization

I want to check if a class is a template specialization of another one. What I have tried is:
template <class T, template <class...> class Template>
struct is_specialization : std::false_type {};
template <template <class...> class Template, class... Args>
struct is_specialization<Template<Args...>, Template> : std::true_type {};
It works fine when all template parameters are type arguments but not when some are non-type arguments. For example it works with std::vector but not std::array (since the later accepts an non-type argument std::size_t).
It's important that the check is made at compile time. Also the solution must work for any template, not just vectors or arrays. That means that it can be any number of type arguments and any number of non-type arguments. For example it should work with template <class A, bool B, class C, int D, class... Args> class foo;
C++20 is a weird, weird world. Cross-checking is welcome as I'm a beginner with CTAD and not entirely sure I've covered all bases.
This solution uses SFINAE to check whether class template argument deduction (CTAD) succeeds between the requested class template and the mystery type. An additional is_same check is performed to prevent against unwanted conversions.
template <auto f>
struct is_specialization_of {
private:
template <class T>
static auto value_impl(int) -> std::is_same<T, decltype(f.template operator()<T>())>;
template <class T>
static auto value_impl(...) -> std::false_type;
public:
template <class T>
static constexpr bool value = decltype(value_impl<T>(0))::value;
};
// To replace std::declval which yields T&&
template <class T>
T declrval();
#define is_specialization_of(...) \
is_specialization_of<[]<class T>() -> decltype(__VA_ARGS__(declrval<T>())) { }>::value
// Usage
static_assert(is_specialization_of(std::array)<std::array<int, 4>>);
First caveat: Since we can't declare a parameter for the class template in any way without knowing its arguments, passing it around to where CTAD will be performed can only be done by jumping through some hoops. C++20 constexpr and template-friendly lambdas help a lot here, but the syntax is a mouthful, hence the helper macro.
Second caveat: this only works with movable types, as CTAD only works on object declarations, not reference declarations. Maybe a future proposal will allow things such as std::array &arr = t;, and then this will be fixed!
Actually fixed by remembering that C++17 has guaranteed copy-elision, which allows direct-initialization from a non-movable rvalue as is the case here!

When specializing a class, how can I take a different number of template parameters?

I just asked this question: Can I get the Owning Object of a Member Function Template Parameter? and Yakk - Adam Nevraumont's answer had the code:
template<class T>
struct get_memfun_class;
template<class R, class T, class...Args>
struct get_memfun_class<R(T::*)(Args...)> {
using type=T;
};
These is clearly an initial declaration and then a specialization of struct get_memfun_class. But I find myself uncertain: Can specializations have a different number of template parameters?
For example, is something like this legal?
template<typename T>
void foo(const T&);
template<typename K, typename V>
void foo<pair<K, V>>(const pair<K, V>&);
Are there no requirements that specializations must take the same number of parameters?
You seem to confuse the template parameters of the explicit specialization and the template arguments you use to specialize the template.
template<class T> // one argument
struct get_memfun_class; // get_memfun_class takes one template (type) argument
template<class R, class T, class...Args>
struct get_memfun_class<R(T::*)(Args...)> {
// ^^^^^^^^^^^^^^^^
// one type argument
using type=T;
}; // explicit specialization takes one template argument
Yes, there are three template parameters for the explicit specialization, but that doesn't mean that the explicit specialization takes three arguments. They are there to be deduced. You can form a single type using multiple type parameters, which is what is happening there. Also consider that you can fully specialize a template:
template <>
struct get_memfun_class<void>;
// ^^^^
// one type argument
Here it's the same thing. Yes, the explicit specialization takes no parameters, but that just means that there is none to be deduced and indeed you are explicitly writing a template parameter (void) and so the amount of template arguments of the specialization match those of the primary template.
Your example is invalid because you cannot partially specialize functions.
Are there no requirements that specializations must take the same number of parameters?
There is; and is satisfied in your example.
When you write
template<class T>
struct get_memfun_class;
you say that get_mumfun_class is a template struct with a single template typename argument; and when you write
template<class R, class T, class...Args>
struct get_memfun_class<R(T::*)(Args...)> {
using type=T;
};
you define a specialization that receive a single template typename argument in the form R(T::*)(Args...).
From the single type R(T::*)(Args...), you can deduce more that one template paramenters (R, T and the variadic Args..., in this example) but the type R(T::*)(Args...) (a method of a class that receive a variadic list of arguments) remain one.
For example, is something like this legal?
template<typename T>
void foo(const T&);
template<typename K, typename V>
void foo<pair<K, V>>(const pair<K, V>&);
No, but (as written in comments) the second one isn't a class/struct partial specialization (where std::pair<K, V> remain a single type), that is legal; it's a template function partial specialization that is forbidden.
But you can full specialize a template function; so it's legal (by example)
template<>
void foo<std::pair<long, std::string>(const std::pair<long, std::string>&);
as is legal the full specialization for get_memfun_class (to make another example)
template<>
struct get_memfun_class<std::pair<long, std::string>> {
using type=long long;
};

Better way to disable argument-based template parameter deduction for functions?

Here is what I want to do:
template <typename T> void f(DisableDeduction<T> obj) {std::cout << obj;}
// Here DisableDeduction<T> aliases T, but in a such way
// that would prevent compiler from deducing T based
// on provided argument.
/* ... */
f<int>(1); // Works.
f(1); // Error, can't deduce template parameter based on argument.
This is how I currently achieve it:
template <typename T> struct DisableDeduction_Internal {using type = T;};
template <typename T> using DisableDeduction = typename DisableDeduction_Internal<T>::type;
It works perfectly (as described), but it introduces one extra helper type.
But can I achieve same result without extra types?
You can do it by putting T in non deducible context (to the left of ::), and use std::common_type from <type_traits>.
example:
template <typename T> void f(typename std::common_type<T>::type obj) {std::cout << obj;}
Since C++20 you can use std::type_identity_t<T>.
Pre-C++20 I would suggest std::enable_if_t<true, T>.
std::common_type_t<T>, while seemingly innocuous, applies std::decay to the type, removing const, volatile, and/or reference-ness.

Template template functions and parameters deduction

I've got a problem with template templates and parameters deduction. Here's the code:
template<typename U, template<typename> class T>
void test(T<U>&& t)
{
...
}
I expected this to accept either lvalues and rvalues, but only works with rvalues. The collapsing rule "T& && = T&" doesn't apply in this case?
Naturally I could declare the lvalue reference function too, but makes the code less readable.
If you're asking why I need this is to use a static_assert to check if T is a particular class. If there's a simpler way to do so I'll be happy to change my code, but I'd like to know if template templates are usable in this way.
Thanks
Unlike typename T, which can be deduced to be a reference type, template<typename> class T can only ever be deduced to be a class template, so T<U> is always deduced to an object type.
You can write your function templated on T then unpack the template type in the static_assert:
template<typename T> struct is_particular_class: std::false_type {};
template<typename U> struct is_particular_class<ParticularClass<U>>: std::true_type {};
template<typename T> void test(T &&) {
static_assert(is_particular_class<std::remove_reference<T>::type>::value, "!");
}