The C++17 standard introduces "template deduction guides". I gather they're something to do with the new template argument deduction for constructors introduced in this version of the standard, but I haven't yet seen a simple, FAQ-style explanation of what they are and what they're for.
What are template deduction guides in C++17?
Why (and when) do we need them?
How do I declare them?
Template deduction guides are patterns associated with a template class that tell the compiler how to translate a set of constructor arguments (and their types) into template parameters for the class.
The simplest example is that of std::vector and its constructor that takes an iterator pair.
template<typename Iterator>
void func(Iterator first, Iterator last)
{
vector v(first, last);
}
The compiler needs to figure out what vector<T>'s T type will be. We know what the answer is; T should be typename std::iterator_traits<Iterator>::value_type. But how do we tell the compiler without having to type vector<typename std::iterator_traits<Iterator>::value_type>?
You use a deduction guide:
template<typename Iterator> vector(Iterator b, Iterator e) ->
vector<typename std::iterator_traits<Iterator>::value_type>;
This tells the compiler that, when you call a vector constructor matching that pattern, it will deduce the vector specialization using the code on the right of ->.
You need guides when the deduction of the type from the arguments is not based on the type of one of those arguments. Initializing a vector from an initializer_list explicitly uses the vector's T, so it doesn't need a guide.
The left side doesn't necessarily specify an actual constructor. The way it works is that, if you use template constructor deduction on a type, it matches the arguments you pass against all deduction guides (actual constructors of the primary template provide implicit guides). If there is a match, it uses that to determine which template arguments to provide to the type.
But once that deduction is done, once the compiler figures out the template parameters for the type, initialization for the object of that type proceeds as if none of that happened. That is, the deduction guide selected does not have to match the constructor selected.
This also means that you can use guides with aggregates and aggregate initialization:
template<typename T>
struct Thingy
{
T t;
};
Thingy(const char *) -> Thingy<std::string>;
Thingy thing{"A String"}; //thing.t is a `std::string`.
So deduction guides are only used to figure out the type being initialized. The actual process of initialization works exactly as it did before, once that determination has been made.
Related
The C++17 standard introduces "template deduction guides". I gather they're something to do with the new template argument deduction for constructors introduced in this version of the standard, but I haven't yet seen a simple, FAQ-style explanation of what they are and what they're for.
What are template deduction guides in C++17?
Why (and when) do we need them?
How do I declare them?
Template deduction guides are patterns associated with a template class that tell the compiler how to translate a set of constructor arguments (and their types) into template parameters for the class.
The simplest example is that of std::vector and its constructor that takes an iterator pair.
template<typename Iterator>
void func(Iterator first, Iterator last)
{
vector v(first, last);
}
The compiler needs to figure out what vector<T>'s T type will be. We know what the answer is; T should be typename std::iterator_traits<Iterator>::value_type. But how do we tell the compiler without having to type vector<typename std::iterator_traits<Iterator>::value_type>?
You use a deduction guide:
template<typename Iterator> vector(Iterator b, Iterator e) ->
vector<typename std::iterator_traits<Iterator>::value_type>;
This tells the compiler that, when you call a vector constructor matching that pattern, it will deduce the vector specialization using the code on the right of ->.
You need guides when the deduction of the type from the arguments is not based on the type of one of those arguments. Initializing a vector from an initializer_list explicitly uses the vector's T, so it doesn't need a guide.
The left side doesn't necessarily specify an actual constructor. The way it works is that, if you use template constructor deduction on a type, it matches the arguments you pass against all deduction guides (actual constructors of the primary template provide implicit guides). If there is a match, it uses that to determine which template arguments to provide to the type.
But once that deduction is done, once the compiler figures out the template parameters for the type, initialization for the object of that type proceeds as if none of that happened. That is, the deduction guide selected does not have to match the constructor selected.
This also means that you can use guides with aggregates and aggregate initialization:
template<typename T>
struct Thingy
{
T t;
};
Thingy(const char *) -> Thingy<std::string>;
Thingy thing{"A String"}; //thing.t is a `std::string`.
So deduction guides are only used to figure out the type being initialized. The actual process of initialization works exactly as it did before, once that determination has been made.
The C++17 standard introduces "template deduction guides". I gather they're something to do with the new template argument deduction for constructors introduced in this version of the standard, but I haven't yet seen a simple, FAQ-style explanation of what they are and what they're for.
What are template deduction guides in C++17?
Why (and when) do we need them?
How do I declare them?
Template deduction guides are patterns associated with a template class that tell the compiler how to translate a set of constructor arguments (and their types) into template parameters for the class.
The simplest example is that of std::vector and its constructor that takes an iterator pair.
template<typename Iterator>
void func(Iterator first, Iterator last)
{
vector v(first, last);
}
The compiler needs to figure out what vector<T>'s T type will be. We know what the answer is; T should be typename std::iterator_traits<Iterator>::value_type. But how do we tell the compiler without having to type vector<typename std::iterator_traits<Iterator>::value_type>?
You use a deduction guide:
template<typename Iterator> vector(Iterator b, Iterator e) ->
vector<typename std::iterator_traits<Iterator>::value_type>;
This tells the compiler that, when you call a vector constructor matching that pattern, it will deduce the vector specialization using the code on the right of ->.
You need guides when the deduction of the type from the arguments is not based on the type of one of those arguments. Initializing a vector from an initializer_list explicitly uses the vector's T, so it doesn't need a guide.
The left side doesn't necessarily specify an actual constructor. The way it works is that, if you use template constructor deduction on a type, it matches the arguments you pass against all deduction guides (actual constructors of the primary template provide implicit guides). If there is a match, it uses that to determine which template arguments to provide to the type.
But once that deduction is done, once the compiler figures out the template parameters for the type, initialization for the object of that type proceeds as if none of that happened. That is, the deduction guide selected does not have to match the constructor selected.
This also means that you can use guides with aggregates and aggregate initialization:
template<typename T>
struct Thingy
{
T t;
};
Thingy(const char *) -> Thingy<std::string>;
Thingy thing{"A String"}; //thing.t is a `std::string`.
So deduction guides are only used to figure out the type being initialized. The actual process of initialization works exactly as it did before, once that determination has been made.
The C++17 standard introduces "template deduction guides". I gather they're something to do with the new template argument deduction for constructors introduced in this version of the standard, but I haven't yet seen a simple, FAQ-style explanation of what they are and what they're for.
What are template deduction guides in C++17?
Why (and when) do we need them?
How do I declare them?
Template deduction guides are patterns associated with a template class that tell the compiler how to translate a set of constructor arguments (and their types) into template parameters for the class.
The simplest example is that of std::vector and its constructor that takes an iterator pair.
template<typename Iterator>
void func(Iterator first, Iterator last)
{
vector v(first, last);
}
The compiler needs to figure out what vector<T>'s T type will be. We know what the answer is; T should be typename std::iterator_traits<Iterator>::value_type. But how do we tell the compiler without having to type vector<typename std::iterator_traits<Iterator>::value_type>?
You use a deduction guide:
template<typename Iterator> vector(Iterator b, Iterator e) ->
vector<typename std::iterator_traits<Iterator>::value_type>;
This tells the compiler that, when you call a vector constructor matching that pattern, it will deduce the vector specialization using the code on the right of ->.
You need guides when the deduction of the type from the arguments is not based on the type of one of those arguments. Initializing a vector from an initializer_list explicitly uses the vector's T, so it doesn't need a guide.
The left side doesn't necessarily specify an actual constructor. The way it works is that, if you use template constructor deduction on a type, it matches the arguments you pass against all deduction guides (actual constructors of the primary template provide implicit guides). If there is a match, it uses that to determine which template arguments to provide to the type.
But once that deduction is done, once the compiler figures out the template parameters for the type, initialization for the object of that type proceeds as if none of that happened. That is, the deduction guide selected does not have to match the constructor selected.
This also means that you can use guides with aggregates and aggregate initialization:
template<typename T>
struct Thingy
{
T t;
};
Thingy(const char *) -> Thingy<std::string>;
Thingy thing{"A String"}; //thing.t is a `std::string`.
So deduction guides are only used to figure out the type being initialized. The actual process of initialization works exactly as it did before, once that determination has been made.
I am struggling a bit with templates; I am trying to write a method that iterates over a range of strings, no matter their type or the container they are kept in. In the following code:
template<template<class> class ContainerType,
typename CharType>
ContainerType<basic_string<CharType>>
foo(typename ContainerType<basic_string<CharType>>::iterator begin,
typename ContainerType<basic_string<CharType>>::iterator end,
CharType letter)
{
return ContainerType<basic_string<CharType>>();
}
int main()
{
vector<string> words;
auto bar = foo(words.begin(), words.end(), 'a');
}
The compiler can't figure out the type of ContainerType.
I must say that I am a beginner when it comes to C++ templates.
Simply speaking, template argument type deduction only works to the right of the last ::, if there is one. Imagine what you're telling the compiler:
I am calling foo() with a certain type. Now I want you to look at all single-parameter class templates which could possibly exist, try to instantiate each of them with all possible types, and see for which of these a nested typedef iterator matches the type I sent to foo. Then use that combination as template arguments.
I believe it's pretty obvious that doesn't work. That's why anything to the left of :: is a non-deduced context, so template parameters in such context don't participate in template argument deduction. And since foo offers no other context, the argument cannot be deduced.
In this example, is it possible to allow the deduction of the template parameters type of the tuple?
#include<tuple>
#include<string>
template<class T1, class T2>
void fun(std::tuple<T1, T2> t, std::string other){}
int main(){
fun(std::tuple<double, int>(2.,3), std::string("other")); // ok
fun(std::make_tuple(2.,3), std::string("other")); // ok, but trying to avoid `make_tuple`
fun({2.,3},std::string("other")); // desired syntax but
// giving compilation error: candidate template ignored: couldn't infer template argument 'T1' void fun(std::tuple<T1, T2> t)
}
I added the second argument other to avoid solutions involving variadic arguments at the level of the function fun. Also, I am trying to avoid the use of make_tuple, at least from the user code (i.e. in main()). In fact it doesn't need to be the tuple type the one involved as long as the "desired syntax" is allowed and somehow its element types can be deduced at later stage.
(Also, although similar, this has nothing to do with initializer_list since it doesn't work at all having dissimilar elements in the braces)
It fails at least with clang 3.2 and gcc 4.7.2. Is there any hope that it would work with the current or a near-future standard? (e.g. a future(?) initializer_tuple.)
(This can be very useful to add expressiveness to function calls, by aggregating subelements, but that can be argued about)
Note: For the example code it seems that std::forward_as_tuple is more appropriate than std::make_tuple so the arguments are not necessarily copied: http://en.cppreference.com/w/cpp/utility/tuple/forward_as_tuple . Still not as nice as if there were a build-in language feature for heterogeneous initializer lists.
No, there is absolutely no way. Deduction fails if the element types are not of the same type. And no deduction is done at all if the parameter is not a std::initializer_list<T> anyway (you are right that initializer_list doesn't have anything to do with the braces you give, but this is the simple rule for deduction to work).
The template parameter values must be deduced by other function parameter positions involving them or must be explicitly specified.
It is the age of C++20 and there is still no general way to do this.
Workaround 1:
There is a partial solution in the case that the bracket syntax it is, for any reason, reserved for a particular combination of arguments: for example
template<class T1 = double, class T2 = int>
void fun(std::tuple<T1, T2> t, std::string other){}
int main(){
fun({2.,3},std::string("other")); // now works at least, but it may not mean what it seems
}
https://godbolt.org/z/xGaEP5EGn
The worst part of this solution is that it lets exchange the double and the int, but I would say that is a problem of the implicit conversions of built-in types.
It works for any version of C++.
Workaround 2:
The only change since the OP is that now there is CTAD (class template argument deduction).
Which means that there is a new partial workaround for this problem, which is
fun(std::tuple{2.,3}, std::string("other")); // ok, but still trying to avoid `tuple`
Fantasy solution:
This inspired me to think that may be this can be done if CTAD is extended in the future with one of these syntax:
The natural extension to CTAD is if this worked:
void fun(std::tuple t, std::string other){} // implicitly a template because std::tuple is a template
More explicitly:
void fun(std::tuple<auto, auto> t, std::string other){} // implicitly a template because std::tuple is a template and `auto`.
or
template<template<typename...> class Tuple = std::tuple>
void fun(Tuple t, std::string other){}
or
template<template<typename...> class Tuple = std::tuple, class T1, class T2>
void fun(Tuple<T1, T2> t, std::string other){}
None of these work currently, but I am fond of the first and the last option.
The first because it is an extension of CTAD (the type is moved from the calling point to the function argument).
Besides CTAD has been shown to work with (non deduced) template template classes.
The last because it is template argument deduction, under some expanded definition of deduction.
The default argument can help deduce the template template argument (std::tuple).