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

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.

Related

disable template member function if return type is an array

https://www.godbolt.org/z/_4aqsF:
template <typename T> struct Container
{
template <typename TPred> T find_if(TPred pred); // the culprit
};
template <typename T> Container<T> MakeContainer(T const &)
{
return Container<T>();
}
int main()
{
auto x = MakeContainer("Hello!");
}
gcc, clang and msvc apparently agree that this cannot compile because find_if would return an array.
(I would have assumed that the member template isn't instantiated since it doesn't get used - apparently, this simplistic view is wrong.)
Why does SFINAE not apply here?
Is there a way to exclude the member template for types where T is not a returnable type?
SFINAE is not in play because the members of the types produced in your MakeContainer return point are not examined during SFINAE of MakeContainer overloads.
SFINAE happens only in an immediate context. Bodies of types and functions are not in-scope and do not cause a subsitution failure.
template <typename T=char[7]> Container<char[7]> MakeContainer(char const (&)[7])
this signature is fine.
Once selected, the Container<char[7]> is instantiated and its methods parsed.
template <typename TPred> char[7] find_if(TPred pred); // the culprit
there is no TPred that could cause this find_if to be a valid method, so your program is ill formed no diagnostic required.
The correct fix is:
template <typename T> struct Container
{
template <typename TPred> T find_if(TPred pred); // the culprit
};
template <class T, std::size_t N> struct Container<T[N]>:
Container<std::array<T,N>>
{
using Container<std::array<T,N>>::Container;
};
of course, Container<std::array<T,N>> itself needs a very special find_if and probably constructors. But at least it doesn't break immediately.
SFINAE removes from overload sets the overloads which would be illegal during template argument deduction.
Here, the overload set contains only one candidate: MakeContainer<const char (&)[7]>. Template argument deduction ends here. No ambiguity. Everything's fine.
Then, the type Container<const char (&)[7]> is instantiated. And it produces a templated function (Container<const char (&)[7]>::find_if) whose signatures are illegal (all of them, since T is deduced in the context of find_if). SFINAE is not at play.
Now, you can add some SFINAE to your container's find_if function by making its return type depend on its template argument. For that, see max66's answer for that.
To use SFINAE on fidn_if you need to use dependent parameters of the function itself, here's a version that SFINAE on non-returnable types:
template <typename TPred, class U = T, typename std::enable_if<
std::is_same<T, U>::value
&& !std::is_abstract<U>::value
&& !std::is_function<U>::value
&& !std::is_array<U>::value
, bool>::type = true>
U find_if(TPred pred);
Try with
template <typename TPred, typename U = T>
U find_if (TPred pred); // the culprit
SFINAE, over methods, doesn't works with templates parameters of the class. Works over templates of the method itself. So you have to make the SFINAE substitution dependant from a template parameter of the method itself.
So not T but U.
If you fear that someone can "hijack" your function explicating the template types as follows
auto x = MakeContainer("Hello!");
x.find_if<int, int>(1);
you can impose that U and T are the same type
template <typename TPred, typename U = T>
typename std::enable_if<std::is_same<U, T>::value, U>::type
find_if (TPred pred) // the culprit

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;
};

Deduce return and parameter types of function pointer passed as non-type template argument [duplicate]

I have such a template in C++
template<typename T, T* P> struct Ptr {};
so I can use it as such:
const int i = 0;
Ptr<int, &i> ptr;
or
Ptr<decltype(i), &i> ptr;
But I don't want to specify the type int or identity i twice, I want to use just
Ptr<&i> ptr;
and let the compiler figure out the int type part by itself.
How can I declare my template to do that ?
I've read this question but the answer is using macros, that's not nice:
template of template c++?
can I do this by just template without macros ? I'm using Visual C++ 2013.
UPDATE
c++17 introduced "P0127R2 Declaring non-type template parameters with auto", allowing to declare a non-type template parameter(s) with auto as a placeholder for the actual type:
template <auto P> struct Ptr {};
That is, P is a non-type template parameter. Its type can be inferred with decltype(P).
auto in a template parameter list is subject to well-known deduction and partial ordering rules. In your case, the type can be constrained to accept pointers only:
template <auto* P> struct Ptr {};
Note that the syntax utilizing auto is sufficient even for more detailed inspection, e.g.:
template <typename F>
struct FunctionBase;
template <typename R, typename... Args>
struct FunctionBase<R(*)(Args...)> {};
template <auto F>
struct Function : FunctionBase<decltype(F)> {};
It's also possible to use the inferred type as a contraint for other template parameters:
template <auto I, decltype(I)... Is>
struct List {};
Old answer
Since you are asking about a pure class template-based solution without the help of macro definitions then the answer is simple: as for now (Dec 2014, c++14) it is not possible.
This issue has been already identified by the WG21 C++ Standard Committee as a need and there are several proposals to let templates automatically infer the type of non-type template arguments.
The closest is N3601 Implicit template parameters:
Implicit template parameters
The purpose of this example is to eliminate the need for the redundant template<typename T, T t> idiom. This idiom is widely used, with over 100k hits on Google.
The goal is to be able to replace a template declaration like template<typename T, T t> struct C; with another declaration so that we can instantatiate the template like C<&X::f> instead of having to say C<decltype(&X::f), &X::f>.
The basic idea is to be able to say template<using typename T, T t> struct C {/* ... */}; to indicate that T should be deduced. To describe in more detail, we consider some extended examples of template classes and functions.
[...]
The key idea is that passing the type of the second template parameter is redundant information because it can be inferred using ordinary type deduction from the second type parameter. With that in mind, we propose that prefacing a template parameter with using indicates that it should not be passed explicitly as a template argument but instead will be deduced from subsequent non-type template arguments. This immediately allows us to improve the usability of describe_field as follows.
template<using typename T, T t> struct describe_field { /* ... */ };
/* ... */
cout << describe_field<&A::f>::name; // OK. T is void(A::*)(int)
cout << describe_field<&A::g>::arity; // OK. T is double(A::*)(size_t)
A similar proposal is the one included in N3405 Template Tidbits:
T for two
The motivating example is a putative reflection type trait giving properties of a class member.
struct A {
void f(int i);
double g(size_t s);
};
/* ... */
cout << describe<&A::f>::name; // Prints "f"
cout << describe<&A::g>::arity; // prints 1
The question is "what should the declaration of describe look like?" Since it takes a non-type template parameter, we need to specify the type of the parameter using the familiar (100k hits on Google) “template<class T, T t>” idiom
template<typename T, T t> struct describe;
[...]
Our key idea is that passing the type of the second template parameter is (nearly always) redundant information because it can be inferred using ordinary type deduction from the second type parameter. With that in mind, we propose allowing describe to be declared as follows.
template<typename T t> struct describe;
/* ... */
cout << describe<&A::f>::name; // OK. T is void(A::*)(int)
cout << describe<&A::g>::arity; // OK. T is double(A::*)(size_t)
The current status of both proposals can be tracked under EWG issue 9.
There are some other discussions proposing alternative syntax with auto:
template <auto T> struct describe;

Universal reference with templated class

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);
}
};

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, "!");
}