Use one argument for template parameter deduction? - c++

Let’s say I have a template function, assign(). It takes a pointer and a value and assigns the value to the pointer’s target:
template <typename T> void assign(T *a, T b) { *a = b; }
int main() {
double i;
assign(&i, 2);
}
In this case I always want T to be deduced from the first argument, but it looks like I didn’t do a good job of expressing this. 2’s type is int, so:
deduce.cpp:5:5: error: no matching function for call to 'assign'
assign(&i, 2);
^~~~~~
deduce.cpp:1:28: note: candidate template ignored: deduced conflicting types for parameter 'T' ('double' vs. 'int')
template void assign(T *a, T b) { *a = b; }
Is there a way I can declare assign() so that the second argument doesn’t participate in template parameter deduction?

Using two type parameters is probably the best option, but if you really want to perform deduction only from the first argument, simply make the second non-deducible:
template<typename T>
void assign( T* a, typename std::identity<T>::type b );
Demo: http://ideone.com/ZW6Mpu
An earlier version of this answer suggested using the template alias feature introduced in C++11. But template aliases are still a deducible context. The primary reason that std::identity and std::remove_reference prevents deduction is that template classes can be specialized, so even if you have a typedef of a template type parameter, it's possible that another specialization has a typedef of the same type. Because of the possible ambiguity, deduction doesn't take place. But template aliases preclude specialization, and so deduction still occurs.

The problem is that the compiler is deducing conflicting information from the first and the second argument. From the first argument, it deduces T to be double (i is a double); from the second one, it deduces T to be int (the type of 2 is int).
You have two main possibilities here:
Always be explicit about the type of your arguments:
assign(&i, 2.0);
// ^^^^
Or let your function template accept two template parameters:
template <typename T, typename U>
void assign(T *a, U b) { *a = b; }
In this case, you may want to SFINAE-constraint the template so that it does not partecipate to overload resolution in case U is not convertible to T:
#include <type_traits>
template <typename T, typename U,
typename std::enable_if<
std::is_convertible<U, T>::value>::type* = nullptr>
void assign(T *a, U b) { *a = b; }
If you do not need to exclude your function from the overload set when U is not convertible to T, you may want to have a static assertion inside assign() to produce a nicer compilation error:
#include <type_traits>
template<typename T, typename U>
void assign(T *a, U b)
{
static_assert(std::is_convertible<T, U>::value,
"Error: Source type not convertible to destination type.");
*a = b;
}

It's just that the value 2 is deduced to the type int, which doesn't match the template parameter deduced by &i. You need to use the value as a double:
assign(&i, 2.0);

Why not just use two independent parameter types, one for the source and one for the destination?
template <typename D, typename S> void assign(D *a, S b) { *a = b; }
int main(int argc, char* argv[])
{
double i;
assign(&i, 2);
return 0;
}
If the assignment is not possible, the template instantiation won't compile.

My attempt would look something like this:
template<typename T, typename U>
typename std::enable_if< std::is_convertible< U&&, T >::value >::type // not quite perfect
assign( T* dest, U&& src ) {
*dest = std::forward<U>(src);
}
the second argument is anything you can convert to a T, but we take it by universal reference and conditionally move it into *dest. I test for convertability in the signature rather than have the body fail to compile, because failure-to-find-an-overload seems more polite than failing to compile-the-body.
Live example.
Compared to the simpler:
template<typename T>
void assign( T* dest, typename std::identity<T>::type src ) {
*dest = std::move(src);
}
the above saves 1 move. If you have an expensive to move class, or a class that is copy-only and expensive to copy, this could save a significant amount.

C++20 has std::type_identity which can be used to establish a non-deduced context:
#include <type_traits>
template <typename T>
void assign(T *a, std::type_identity_t<T> b) {
*a = b;
}
int main() {
double i;
assign(&i, 2);
}
Demo

Alternatively, you can use decltype to typecast the second argument to be the type of first.
template <typename T> void assign(T *a, T b) { *a = b; }
int main() {
double i;
assign(&i, (decltype(i))2);
}

Apparently std::identity is not there anymore (Is there a reason why there is not std::identity in the standard library?)
But you can specify the parameter type in the parameter type list, when calling the function:
template <typename T> void assign(T *a, T b) { *a = b; }
int main() {
double i;
assign<double>(&i, 2);
}
In this way the compiler will convert the integer input argument to double to match the function template, without argument deduction.
Live demo

Related

Understanding C++ template instantiation - one implementation matches and the other does not [duplicate]

Can any one explain me the below
a few constructs are not deduced contexts
Qualified type names. A type name like Q<T>::X will never be used to
deduce a template parameter T , for example.
Nontype expressions that are not just a nontype parameter. A type
name like S<I+1> will never be used to deduce I , for example. Neither
will T be deduced by matching against a parameter of type
int(&)[sizeof(S<T>)] .
These limitations should come as no surprise because the deduction
would, in general, not be unique (or even finite).
Why the T in Q<T>::X can't be deduced ? and why int(&)[sizeof(S<T>)] called as a Nontype expression, even it has the type parameter in it.
contents were taken from :"C++ Templates The Complete Guide"
EX(from book)
template <int N>
class X {
public:
typedef int I;
void f(int) {}
};
template<int N>
void fppm(void (X<N>::*p)(X<N>::I));
int main()
{
fppm(&X<33>::f); // fine: N deduced to be 33
}
In the function template fppm() , the subconstruct X<N>::I is a
nondeduced context.
Edit
I've read some answers which trigger me an another other question
template<typename T> void(T p1,typename foo<T>::subtype P2)
in the above deceleration if foo<T> is not deduced which subtype the
compiler will choose for p2?
EDIT2
i am trying to below
#include <iostream>
#include <cstring>
template <typename T>
struct foo{
using type=int;
void print(type a){
std::cout<<a<<"\n";
}
};
template<>
struct foo<float>{
using type=float;
void print(type a){
std::cout<<a<<"\n";
}
};
template<typename T2>
void test(foo<T2> o,typename foo<T2>::type p){
o.print(p);
}
int main(){
foo<float> o;
test(o,1.2);
}
It's silly but I wonder what is the type of the T2 after test(o,1.2)
Consider the following example:
template <typename T>
struct C
{
using type = T;
};
template <> // specialization
struct C<float>
{
using type = int; // oops
};
template <> // another specialization
struct C<char>
{
char type; // type is not even a type now
};
template <typename T>
void foo( typename C<T>::type x ) {}
template <typename T>
void foo( C<T>, typename C<T>::type x ) {}
int main()
{
// foo(3); // there are two parameter T (int & float)
// that both would deduce to int for type.
// Which T should the compiler chose?
foo<int>(3); // explicitly naming the type works.
foo<float>(3);
foo( C<int>{}, 3); // works
foo( C<float>{}, 3); // works
// foo( C<char*>{}, 3); // doesn't work, conflicting types
foo( C<char*>{}, "hello"); // works
}
This examples should make clear why this construct is not possible. Its easy to construct ambiguities which are impossible to resolve for the compiler.

C++17 Partial Deduction Guide

I am trying to write a deduction guide, that only detects one of many typename's from given constructor argument and requires user to enter int size manually
template <int size, typename T>
struct Board
{
array<array<T, size>, size> values;
explicit Board(const vector<T>& raw_values){
}
};
template <int size, typename T> Board(const vector<T>&) -> Board<int size, T>;
The idea above is that user should still be forced to enter "int size" argument of template, but "typename T" should be deduced from the argument of constructor, is this possible?
After correct specification, this is how method should be called
auto b = Board<3>(initialStateVector);
Currently, it requires to me to enter like this;
auto b = Board<3, int>(initialStateVector);
So basically, I want "int" above to be deduced from given initialStateVector, which has type
const vector<int>& raw_values
The idea above is that user should still be forced to enter "int size" argument of template, but "typename T" should be deduced from the argument of constructor, is this possible?
According a note (and following examples) in this cppreference page
Class template argument deduction is only performed if no template argument list is present. If a template argument list is specified, deduction does not take place.
no, this isn't possible (not in C++17; we can hope in future versions of the standard).
If you want explicit the size and let deduce the type, the best I can imagine is pass through a good-old make_something function.
I mean something as follows (using std::size_t for the size, as in std::array and almost all STL)
template <std::size_t S, typename T>
Board<S, T> make_Board (std::vector<T> const & v)
{ return {v}; }
// ...
auto b = make_Board<3>(initialStateVector);
that should works also in C++11.
I came up with a workaround using a size hint object
template<int size>
struct SizeHint {};
Your class would take this as an additional constructor argument:
Board(SizeHint<size>, const std::vector<T>& raw_values)
You call the constructor like this:
auto b = Board(SizeHint<2>{}, v);
Bonus
This approach also works for type hints (my original motivation how I found this thread):
template<typename T>
struct TypeHint{};
template<typename Result, typename T>
struct S {
S(TypeHint<Result>, T arg) : t{arg}{}
Result r() {return t;}
T t;
};
#include <iostream>
int main() {
S s{TypeHint<int>{}, 5.7};
std::cout << s.r() << std::endl;
}
This can also be used in combination with variadic templates:
template<typename Result, typename... Args>
struct S {
S(TypeHint<Result>, Args... args) : t{args...}{}
std::tuple<Args...> t;
};

Template array length as argument

I am trying to learn more about templates in C++. I would like to be able to call a function where I pass it a type and the length as an argument. Is this possible?
template <class T>
void alloc_arr (int l) {
std::allocator<T[l]> a;
}
alloc_arr<int[]>(64);
It doesn't work because the instantiated type must be fixed at compile time (T[l] is not fixed).
Is there some other way to do this which doesn't require the length to be specified in the type (<T[64]>)?
Is there some other way to do this which doesn't require the length to be specified in the type ()?
In some way, you need to pass it as template parameter
You can pass it explicitly, as suggested by Lourens Dijkstra
template <typename T, std::size_t Dim>
void alloc_arr ()
{
std::allocator<T[Dim]> a;
// ...
}
or, if you can use at least C++11, also you can deduce it from the type of an argument; by example,
template <typename T, std::size_t Dim>
void alloc_arr (std::integral_constant<std::size_t, Dim> const &)
{
std::allocator<T[Dim]> a;
// ...
}
or also
template <typename T, typename U>
void alloc_arr (U const &)
{
std::allocator<T[U::value]> a;
// ...
}
calling alloc_arr with a std::integral_constant<std::size_t, 5u>{}, by example.
You could pass the size as a template parameter:
template <class T, size_t size>
void alloc_arr() { ... }
This is the only way. A couple of days ago I found out that passing a constexpr lambda as a regular parameter is considered ill-formed: Trying to pass a constexpr lambda and use it to explicitly specify returning type
Also, note that type T should be int; not int[].
So, calling alloc_arr:
alloc_arr<int, 64>();

c++: why template cannot be used to deduce both container and element type?

I've got a very simple test program like below:
#include<vector>
#include<iostream>
using namespace std;
template<typename C, typename E>
void f(const C<E>& container){
cout<<container.size()<<endl;
}
int main(){
vector<int> i;
f(i);
return 0;
}
It fails to compile with gcc 4.1.2. Error message is:
templateContainer.cpp:5: error: ‘C’ is not a template
templateContainer.cpp: In function ‘int main()’:
templateContainer.cpp:10: error: no matching function for call to ‘f(std::vector<int, std::allocator<int> >&)’
You could use a template template parameter (and note that std::vector actually takes more than one template parameter [an element type, and an allocator type]).:
template<template <typename...> class C, typename... E>
void f(const C<E...>& container){
cout<<container.size()<<endl;
}
Live Demo
If you don't need the type decompositions, you could simply use an ordinary template.
template<typename C>
void f(const C& container){
cout<<container.size()<<endl;
}
You can additionally obtain typedefs from STL containers: for example, if you want to know the type of elements held by the container, value_type is there for you.
template<typename C>
void f(const C& container){
using ValueType = typename C::value_type;
cout<<container.size()<<endl;
}
std::vector has two template arguments, type and allocator.
template <template<class, class> class C, class E, class A>
void f(const C<E, A> &container)
{
std::cout << container.size() << endl;
}
int main()
{
std::vector<int> i;
f(i);
return 0;
}
Although WhiZTiM's answer is correct (well, preferring the second part), it doesn't explain why your code doesn't work.
Assuming for the moment that you intended roughly
template<template <typename> class C, typename E> void f(const C<E>&);
the reason that std::vector doesn't match is that it is the wrong shape - it has two type parameters, not one as in your declaration.
Just because you don't often explicitly write the defaulted second (allocator) param, doesn't mean it isn't there.
For comparison, this works (or doesn't) in an analogous way:
void f(int);
void g(int, int* = nullptr);
void apply(void (*func)(int), int);
apply(f, 42); // ok - f matches shape void(*)(int)
apply(g, 42); // error - g is really void(*)(int,int*)
specifically, default arguments (or type parameters) are syntactic sugar. They allow you to forget about those arguments at the call (instantiation) site, but don't change the shape of the function (or template).

How do I fix c++ compile with wrapped typedef in a template structure

This code will fail with error message (line numbers are off). How do I fix this (keeping same intent)?
g++ -o c_test c_test.cpp
c_test.cpp: In function 'int main(int, char**)':
c_test.cpp:28:18: error: no matching function for call to 'wcalc(CWrapped<5>::u_type&)'
c_test.cpp:28:18: note: candidate is:
c_test.cpp:17:58: note: template int wcalc(typename CWrapped::u_type)
The wrapped type is passed to both "calc" and "wcalc" function, but the 2nd one fails.
I want to be able to wrap the types so I can use a compile-time define to specify different types but still use the same wrapped function
// Example template class
template <int T_B>
class m_int {
public:
int x;
m_int() { x = T_B; }
int to_int() { return(x); }
};
// Desired Typedef wrap
template <int T_BITS> struct CWrapped {
typedef m_int<T_BITS> u_type;
};
// This is ok, no wrapping
template <int T_BITS> int calc(m_int<T_BITS> x) {
return(x.to_int());
}
// This fails when instantiated
template <int T> int wcalc(typename CWrapped<T>::u_type x) {
return(x.to_int());
}
int main(int argc, char* argv[]) {
CWrapped<5>::u_type s;
int x = calc(s);
int y = wcalc(s);
return(0);
}
From the C++11 Standard, paragraph 14.8.2.5/16
"If, in the declaration of a function template with a non-type template parameter, the non-type template parameter is used in a subexpression in the function parameter list, the expression is a non-deduced context as specified above. Example:"
template <int i> class A { / ... / };
template <int i> void g(A<i+1>);
template <int i> void f(A<i>, A<i+1>);
void k() {
A<1> a1;
A<2> a2;
g(a1); // error: deduction fails for expression i+1
g<0>(a1); // OK
f(a1, a2); // OK
}
"Note: Template parameters do not participate in template argument deduction if they are used only in non-deduced contexts. For example:"
template<int i, typename T>
T deduce(typename A<T>::X x, // T is not deduced hereT t, // but T is deduced here
typename B<i>::Y y); // i is not deduced here
A<int> a;
B<77> b;
int x = deduce<77>(a.xm, 62, b.ym);
// T is deduced to be int, a.xm must be convertible to
// A<int>::X
// i is explicitly specified to be 77, b.ym must be convertible
// to B<77>::Y
Because of the above, your non-type template parameter T cannot be deduced: you have to provided it explicitly:
int main(int argc, char* argv[]) {
CWrapped<5>::u_type s;
int x = calc(s);
int y = wcalc<5>(s); // Template argument 5 cannot be deduced!
return(0);
}
Also see this related link: C++, template argument can not be deduced (courtesy of #NateKohl)
Your problem is that CWrapped is a map from int to u_type in a dependent context.
You need a map from u_type to int in order for type deduction to work.
Template type deduction is simple pattern matching, it doesn't invert arbitrary template constructs.
As an example, here is how you can extract which CWrapped would resolve to T:
template<typename T>
struct GetWrapped;
template<int N>
struct GetWrapped< m_int<N> > {
typedef CWrapped< N > type;
};
template <int T> int wcalc(T const& x) {
typedef typename GetWrapped<T>::type CWrappedType; // assuming you need it for something
return(x.to_int());
}
and here is how you can only accept T for which there is such a mapping at overload resolution:
#include <type_traits>
template <int T> auto wcalc(T const& x)->typename std::enable_if< sizeof(typename GetWrapped<T>::type), int>::type {
return(x.to_int());
}
which uses SFINAE and C++11 features to check that GetWrapped<T> is a valid construct.
Now, this is probably an XY question, as in you asked X but you really needed to know Y. I'm hoping one of the above will help.