A few days I literally discovered a behaviour of C++, where template arguments are automatically inserted, as shown in this example (nonsensical, only used to show what I mean):
#include <iostream>
template<typename Type> void setVar(Type& subj, const Type& in)
{
subj = static_cast<Type>(in);
}
int main()
{
int foo;
setVar(foo, 42);
std::cout << foo << std::endl;
}
My questions:
What is this behaviour called?
are there special rules when and why templates can be automatically inserted?
What is this behaviour called?
Template argument deduction.
are there special rules when and why templates can be automatically inserted?
You cannot say like templates are inserted. Rather the types of parameters are automatically deduced from the arguments. When and how? That's what TAD is all about.
Check out section 14.8.2 in C++03
It's called template argument deduction, and of course there are special rules. Many rules. In 14.8.2 of the standard [temp.deduct].
The summary version is that if there's a set of template arguments which allows the function to be called, then it will be called with those arguments. The complication is exactly what's allowed, and how to choose between possible alternatives.
Related
If I have a function template that has default argument for its template parameter and that function takes a non-default parameter of the type parameter then what's the point in the language to allow that default argument that'll never be used? :
template <class T = int>
void foo(T x){cout << x << endl;}
int main()
{
foo("hi"); // T is char const *
foo(); // error
}
As you can see T=int can never be used because the function doesn't have a default parameter thus the compiler in this context always deduces the type of T from the argument passed to foo.
But it can be used. Here's an example.
auto* foo_ptr = &foo<>; // The default template argument is used.
A function call expression is not the only context where a function template's arguments need to be figured out.
Although default parameters are usually used for non deduced parameters, taking the address of the function (&foo) uses them too.
Another example:
#include <typeinfo>
#include <iostream>
using namespace std;
template <class T = int>
void coutType() {
cout << typeid(T).name() << endl;
}
int main() {
// default
coutType();
// non-default
coutType<double>();
}
Output with Clang++
int
double
what's the point in the language to allow [X]?
Better: what would be the point in prohibiting [X]?
There is value in simplicity. I would give the burden of proof to the side that wants to make the language more complicated. The language allows a template parameter to have a default value. The language allows a template parameter to be deduced when the function is directly invoked. It is simpler to allow these to co-exist than to add a prohibition against using both. Hence I would ask why prohibit, rather than ask why allow. If there is no compelling reason for the complication, then stick to simple. Being allowed to do something does not force one to do it. And maybe someone (like StoryTeller and Dani) will find a use for that something.
Of course, simplicity is not the ultimate criterion. If harm would come from [X], then that would likely outweigh simplicity concerns. Complications can be justified. However, complications should not be introduced just because something seems useless.
On the other hand, one could reasonably ask if [X] can be put to use. And maybe that was the real question, even if the OP did not realize it. Still, I thought I would put up one answer addressing the question-as-phrased.
In the following code sample, I define a class DT (my default type) which I want to be able to pass as the parameter(s) for an arbitrary template. In this example, I pass DT as the key and value parameters for std::map. I don't actually ever try to instantiate map<DT,DT>, I just want to use map<DT,DT> as the template parameter for a templatized function (in this sample, the function f()) that never actually references the type -- it's only used to produce a type-specific instance of the function. (Note that you can't actually instantiate a std::map<DT,DT> since the key of a map must be comparable, but DT is not.)
#include <iostream>
#include <map>
using namespace std;
class DT {};
template <typename T>
string f() {
return "foo";
}
int main() {
cout << f<map<DT,DT>>() << endl;
return 0;
}
This seems to work fine using g++. I even tried passing DT for all four map parameters (overriding the default comparator, and allocator types). Still works. But I'm worried that this technique might fail with some other template, or with some other compiler. So my question is: is this always safe for any template on any c++ compiler compliant with, say, the c++11 standard (and later standards). In other words, is it always safe to pass a completely incompatible type as the parameter for a template, so long as you never try to instantiate that template?
If you're wondering why on earth I'd want to do such a thing, I'm trying to setup a class where I can store type-dependent configuration strings. It will have these two methods:
template<typename T>
const string& get<T>() const;
template<typename T>
void set<T>(const string& value);
I have it largely working to my satisfaction. It has several nice features. For example, types int, const int, int&, const int&, etc are all treated as the same type (which is what I want). And you can store a configuration string for a base class that is later retrievable by a derived type if no entry is found for the more specific derived type. But for the case of say, a std::map, I'd like to be able to store a default configuration string using type map<DT,DT> that will later be returned as a match for any map<Key,Value> when no entry is found for the specific map type at hand. If the above code is valid, then I think I can produce the desired behavior.
Unfortunately, I believe, the standard does not guarantee that std::map<DT, DT> will not be instantiated. [temp.inst]/1 only specifies that
Unless a class template specialization has been explicitly instantiated or explicitly specialized, the class template specialization is implicitly instantiated when the specialization is referenced in a context that requires a completely-defined object type or when the completeness of the class type affects the semantics of the program. […]
Note that this only tells us when a template is guaranteed to be instantiated, it does not give any guarantees that the template will not be instantiated if such an instantiation is not required. [temp.inst]/10 only gives such a guarantee for
[…] a function template, a variable template, a member template, a non-virtual member function, a member class, a static data member of a class template, or a substatement of a constexpr if statement ([stmt.if]), unless such instantiation is required. […]
Note the absence of class templates from this list. Thus, I believe, a compiler would theoretically be allowed to instantiate std::map<DT, DT> even thought it would not be necessary to do so. If instantiating the template std::map with DT as key and value type would be invalid, you'd have a problem. I am unable to find any guarantees concerning instantiating std::map with a key type that does not support a comparison operator. While I would expect this to just work with basically any implementation, I do think that an implementation would theoretically be allowed to, e.g., have a static_assert that checks whether the key type does fulfill the necessary requirements. [res.on.functions]/1 would seem to apply (emphasis mine):
In certain cases (replacement functions, handler functions, operations on types used to instantiate standard library template components), the C++ standard library depends on components supplied by a C++ program. If these components do not meet their requirements, the Standard places no requirements on the implementation.
Thus, I think that, strictly speaking, the standard does not guarantee that using std::map<DT, DT> will work…
If you simply want to use std::map<DT, DT> as a sort of tag type to indicate a special case, I would suggest to just not use std::map but something else, for example:
template <typename, typename>
struct default_config_tag;
and then default_config_tag<DT, DT> or just DT as your tag (not sure if you need the argument to be an instance of a template with two type parameters) should be sufficient…
You've already got the answer to your question, but the context of the question is likewise interesting for readers of this post, so I thought it'd be valuable to mention that you may use tag dispatching for a use case such as your own, to:
Set up default configuration strings, at compile time, for specific types (e.g. int) or groups of types (e.g. std::map<K, V> for generic K and V)
Without tag dispatching, this can be tricky as you may not partially specialize a function template.
E.g.:
#include <map>
#include <string>
#include <iostream>
template <typename T>
class Config {
public:
static const std::string& get() { return Config::getString(); }
static void set(const std::string& value) { Config::getString() = value; }
Config(Config const&) = delete;
void operator=(Config const&) = delete;
private:
static std::string& getString() {
static std::string s(defaultString(dispatch_tag<T>{}));
return s;
}
template <typename U>
struct dispatch_tag {};
// Default string unless specified for specific types below.
template <typename U = T>
static constexpr std::string_view defaultString(dispatch_tag<U>) {
return "default";
}
// Default config strings for a select number of types.
static constexpr std::string_view defaultString(dispatch_tag<int>) {
return "default int";
}
template <typename K, typename V>
static constexpr std::string_view defaultString(
dispatch_tag<std::map<K, V>>) {
return "default map";
}
};
int main() {
std::cout << Config<int>::get() << "\n"; // default int
std::cout << Config<std::string>::get() << "\n"; // default
std::cout << Config<std::map<int, int>>::get() << "\n"; // default map
Config<int>::set("custom int");
Config<std::map<int, int>>::set("custom int-int map");
std::cout << Config<int>::get() << "\n"; // custom int
std::cout << Config<std::map<int, int>>::get() << "\n"; // custom int-int map
std::cout << Config<std::map<int, char>>::get() << "\n"; // default map
}
This does not solve, however, that you would like to (based on your comments to your own post) specify, at run-time, the value of the fall-back default configuration string for generic types (say, std::map<K, V>).
The following code compiles with VS15 Community and prints out "Hello".
#include <functional>
#include <iostream>
template<typename T>
using void_template_alias_t = void;
template<typename T>
using Func = std::function<void( T )>;
template<typename T>
using FuncVoid = Func<void_template_alias_t<T>>;
int main()
{
FuncVoid<void> hello = [] { std::cout << "Hello\n"; };
hello();
}
I think this is not allowed to compile.
I was playing around, the code was a bit more complex. I naively expected this to work, but suddenly realized that this code should not compile because you can't make a Func<void> (or am I wrong with this?).
Did I found a magic workaround?
Is this a new behavior from the C++14 standard?
Or is it simply a compiler bug?
Edit: The following more simplified version does not compile.
#include <functional>
#include <iostream>
template<typename T>
using Func = std::function<void( T )>;
int main()
{
Func<void> hello = [] { std::cout << "Hello\n"; };
hello();
}
So why is the code above compiling and working as I first expected?
Is it a correct implementation, if not, how would it look like?
Or is it simply a compiler bug?
That. As mentioned by #T.C., CWG #577 is relevant:
[…] there
was some concern expressed over the treatment of function templates
and member functions of class templates if the C++ rule were changed:
for a template parameter T, would a function taking a single parameter
of type T become a no-parameter function if it were instantiated with
T = void?
This is a justified complaint, but unfortunate for you, member functions/member function templates and type-ids were affected equally by the resolution:
A parameter list consisting of a single unnamed parameter of non-dependent type void is equivalent to the an empty parameter list.
thus both your snippets are ill-formed, as the parameter's type is indeed dependent.
Is it a correct implementation, if not, how would it look like?
There is no correct implementation. If you need a function type with an empty parameter list, you'll have to specify that independent from template parameters.
So why is the code above compiling and working as I first expected?
My best guess: VC++ does the substitution of void_template_alias_t<T> after assuring that the parameter type is not a dependent type "cv void", but before making the "void -> empty list transformation". However, it's often hard to comprehend how VC++ (or any compiler, for that matter) thinks internally.
I've seen code like this:
#include <iostream>
// member_function_templates.cpp
struct X
{
template <class T> void mf(T* t) {
std::cout << "in mf t is: " << *t << std::endl;
}
};
int main()
{
int i = 5;
X* x = new X();
x->mf(&i); //why can this be called without specifying a type as in: x->mf<int>(&i) ??
}
My question is in the comment there in main. Why can you call:
x->mf(&i);
...without specifying a type? Intuition says it should be called like:
x->mf<int>(&i);
... but apparently does not have to be called like this (the above compiles with gcc 4.7 for me either way - with of without explicitly specifying the template.)
And, if you call it without specifying a type for the template, what will the type of T be (in the template function definition)? (I'm guessing it defaults to the type of whatever it is you pass into the function mf as an argument, but further explanation of how this works would be nice)
That is template type deduction and it applies to all template functions, not just members of a non-templated class. Basically the compiler is able to infer (following a set of rules in the standard) what the type T means from the arguments to the function. When the compiler sees the call to mf(&i);, it knows that i is an int, so the argument is an int* and that means that you want the overload that has T==int (*)
You can still provide a particular set of template arguments if you want to force a particular specialization of the template. If, for example, you want the parameter to the template not be the deduced one, but one that is convertible from it:
template<typename T>
void foo(T arg) {...}
int i = 10;
foo(i); // calls foo<int>(i)
foo<double>(i); // calls foo<double>(static_cast<double>(i))
(*) It is slightly more complicated than this, as there could be potentially multiple T types for which the argument would be valid, but the rules in the language determine what particular T will be deduced. In this case, it is int.
I can write a templated function this way
template<class T> void f(T x) {…}
or this way
template<class T> void f(T const& x) {…}
I guess that the second option can be more optimal as it explicitly avoids a copy, but I suspect that it can also fail for some specific types T (eg functors?).
So, when should use the first option, and when to use the second? There are also this boost::call_traits<T>::param_type and boost::reference_wrapper that were in the answers to my previous question, but people don't use them everywhere, do they? Is there a rule of thumb for this? Thanks.
Is there a rule of thumb for this?
The same general rules for when to use pass by reference vs. pass by value apply.
If you expect T always to be a numeric type or a type that is very cheap to copy, then you can take the argument by value. If you are going to make a copy of the argument into a local variable in the function anyway, then you should take it by value to help the compiler elide copies that don't really need to be made.
Otherwise, take the argument by reference. In the case of types that are cheap to copy, it may be more expensive but for other types it will be faster. If you find this is a performance hotspot, you can overload the function for different types of arguments and do the right thing for each of them.
I suspect that it can also fail for some specific types
Pass by reference-to-const is the only passing mechanism that "never" fails. It does not pose any requirements on T, it accepts both lvalues and rvalues as arguments, and it allows implicit conversions.
Thou shalt not wake the dead, but head a similar problem and here's some example code that shows how to use C++11s type traits to deduce whether a parameter should be passed by value or reference:
#include <iostream>
#include <type_traits>
template<typename key_type>
class example
{
using parameter_type = typename std::conditional<std::is_fundamental<key_type>::value, key_type, key_type&>::type;
public:
void function(parameter_type param)
{
if (std::is_reference<parameter_type>::value)
{
std::cout << "passed by reference" << std::endl;
} else {
std::cout << "passed by value" << std::endl;
}
}
};
struct non_fundamental_type
{
int one;
char * two;
};
int main()
{
int one = 1;
non_fundamental_type nft;
example<int>().function(one);
example<non_fundamental_type>().function(nft);
return 0;
}
Hope it helps others with a similar issue.
Besides what James McNellis wrote, I just want to add that you can specialize your template for reference types (for example like this)
boost::traits has a type trait that selects the "best" type, based on T:
call_traits<T>::param_type
As already mentioned, there are no template-specific issues.