I have simple code about template function for Visual C++ 6.0 and Visual Studio 2008.
#include <stdio.h>
#include <vector>
template<typename T>
void function(const std::vector<T> &vec)
{
printf("vector version\n");
}
template<typename T>
void function(T val)
{
printf("value version\n");
}
int main()
{
std::vector<int> vec;
function(vec);
return 0;
}
I tried for each environment, and finally get
at VC6, function of value version, and
at VS2008, function of vector version.
I have 2 questions.
I have recognized priority of overloaded function call as following,
a) specialized function (without implicit type convert)
b) template function (without implicit type convert)
c) specialized function, with implicit type convert
d) template function, with implicit type convert
with this rule, the above results seems that
at VC6, b) is accepted (with <T> = std::vector<int>)
at VS2008, b) is ignored(?) and d) is accepted(?) (with <T> = int)
This means that VC6 is valid and VS2008 is wrong.
Is not my guess correct?
Although, I wish vector version is called for both VC6 and VS2008.
Can I do it?
Regards.
Actually VC6 is wrong; MS had limited support for the C++99 standard (which is when templates were standardized) in VC6 and had better support in VS2005 and up.
Calling function(vec) calls
template<typename T>
void function(const std::template vector<T>& vec)
with T as an int type because the template was deduced from the vector template type (the same as calling function<int>(vec)). If you called function(&vec) then the value function will be called since you're passing in a reference, which gets deduced to function<std::vector<int>>(vec).
If you want it to always call the proper function, then you'll need to be explicit, so you'd need to call it as such:
function< std::vector<int> >(vec)
Which will deduce to the vector version. Note the space between the >, this is to avoid the compiler thinking you meant the stream operator >>.
Hope that helps.
Related
I'm following an online course (Pluralsight.com) in C++ and struggling with an exercise on templates, in particular passing an actual number (e.g. 3) to a template which expects (T& t).
The code written by the instructor will not compile on my PC - I believe I understand why it will not compile, but want to how it compiled for the instructor (who demos it) and how you should handle this case.
#include "stdafx.h"
#include <iostream>
template <class T>
T myMax(T& t1, T& t2)
{
return t1 < t2 ? t2 : t1;
}
int main()
{
int result = myMax(3, 4);
return result;
}
EDIT: Fixed typo in code (from my experimenting before asking the question). It seems to run in https://ideone.com/1cjJUD so I'm now not sure why it won't compile for me! (Thanks #user4581301)
EDIT2 Post-answer, change name of function to avoid confusion. Should make question clearer to anyone else who stumbles upon the same thing. https://ideone.com/dZffn6
The compiler error is "C2664 'T max<int>(T &,T &)': cannot convert argument 1 from 'int' to 'int &"
This looks to me like it is failing since 3 is an actual number not a reference to a number stored somewhere. Declaring int a=3; and passing a to my max function works fine.
Am I correct about the reason for the compile failure?
How does the instructors code compiles without error? Has this changed in more recent C++ versions (I believe the class uses C++03)?
Is it possible to write a template where I can pass either ByRef or ByVal? Would I end up with 4 cases (a, b); (a&, b); (a, b&); (a&, b&) ?
Because you are returning by value, there's no reason for the parameters to be non-const. rvalues (including literals and temporary objects) are compatible with const references. So that would be a good way to fix it without dealing with all possible combinations:
template <class T>
T myownmax(T const& t1, T const& t2)
{
return t1 < t2 ? t2 : t1;
}
Note that I've renamed the function to avoid ambiguity with std::max. Neil mentioned removing the using namespace std; in a comment -- while that line does trigger the problem, removing it is not a complete solution.
In particular, even without using namespace std;, the code std::complex<double> a, b; auto c = max(a, b); will still find std::max, defeating the whole purpose of making your function a template so it works with any type! This is a consequence of "argument dependent lookup".
So using a different name is the best option.
I have a bubble-sort function that takes an array, a compare function, and a boolean that indicates if it should sorts the array upside-down. It is a template function that supports any data-type, and will deduce array size automatically.
When specifying the compare function, if I pass function pointer, the compiler will deduce the data type of array automatically, which is great. But if I pass a lambda instead, it will not deduce automatically. I have to specify the data type explicitly, or static_cast the lambda as fnCompare_t<double>.
What is the reason behind this? Because according to this post, as long as the lambda doesn't capture, it can be used like the plain-old function pointer, but it seems it is not always the case? How come it can be different in this case?
#include <iostream>
using namespace std;
template <typename T>
using fnCompare_t = int(*)(T const &, T const &);
template <typename T, size_t count>
inline void BubbleSort(
T(&array)[count],
fnCompare_t<T> fnCompare,
bool reverse)
{
cout << "TODO: Implement BubbleSort" << endl;
}
double doubleArray[] = {
22.3, 11.2, 33.21, 44.2, 91.2, 15.2, 77.1, 8.2
};
int CompareDouble(double const & a, double const & b)
{
return a > b ? 1 : a == b ? 0 : -1;
}
int main()
{
auto fnCompare = [](double const & a, double const & b) -> int {
return a > b ? 1 : a < b ? -1 : 0;
};
// compile OK:
BubbleSort(doubleArray, CompareDouble, false);
BubbleSort(doubleArray, static_cast<fnCompare_t<double>>(fnCompare), false);
BubbleSort<double>(doubleArray, fnCompare, false);
// compile error, could not deduce template argument:
//BubbleSort(doubleArray, fnCompare, false);
return 0;
}
The reason why is because you can't get an implicit conversion on a templated parameter when using deduction. The classic example is:
template <class T>
T min(T x, T y);
Calling this function as min(1, 3.0) will result in a failure. Because for both arguments, it tries to find a T to get a perfect match, and fails. If you specify the template parameter explicitly it can work: min<double>(1, 3.0). The same is true in your example, if you specify T explicitly it will work.
The idiomatic way to write the signature for your function is:
template <typename Iter, typename F>
inline void BubbleSort(
Iter b, Iter e,
F fnCompare,
bool reverse)
However, this discards the compile time length information. If you want to keep that, you can do:
template <typename T, size_t count, typename F>
inline void BubbleSort(
T(&array)[count],
F fnCompare,
bool reverse);
Though you should at least consider using std::array instead of a C style array which will make the signature a bit less ugly and has other benefits.
This may seem odd as we are not "verifying" the comparator having the correct signature, in the signature of our sort. But this is normal in C++, if the comparator is incorrect then it will fail at the point of usage and it will still be a compile time error. Note as well when you try to depend on a lambda implicitly converting into a function pointer, you are being unnecessarily restrictive: lambdas only convert into function pointers with identical signature. Even if the output of the lambda is implicitly convertible to the output of the function pointer, your lambda will not implicitly convert, even though the lambda can still be used!
As a final final note, it's usually better to pass functors because it's better for performance. Comparators are usually small functions and often will get inlined. But in your version, the comparator will not typically be inlined, in mine it will (because I preserve the original type of the lambda, and you do not).
You need to explicitly cast the lambda to a function pointer. There is no other way around it. But, instead of static_casting you can apply + to the lambda, which would trigger the function pointer conversion, as you can apply + to a pointer type:
BubbleSort(doubleArray, +fnCompare, false);
// ^^
// applying unary + invokes the function pointer conversion operator
The reason for why there is no implicit call to the conversion operator is that during overload resolution, the compiler will only consider templates that match perfectly (see this for more info). Because a lambda is not a function pointer, there cannot be a perfect match, and the overload is discarded.
c++ newbie question - How can the C++ compiler know that the parameter to a template function has has STL methods as members? in C# you tell a generic method that a parameter has a type constraint, most commonly. it must implement an interface, but with c++ templates there is no restriction on the parameter type.
#include <list>
#include <iostream>
using namespace std;
template <typename T>
void DisplayContents (const T& Input)
{
for (auto iElement = Input.cbegin() // no intellisense
; iElement != Input.cend()
; ++ iElement )
cout << *iElement << ' ';
cout << endl;
}
int main ()
{
std::list <int> listIntegers;
listIntegers.push_front (10);
listIntegers.push_front (2011);
listIntegers.push_back (-1);
listIntegers.push_back (9999);
DisplayContents(listIntegers);
// DisplayContents(99); // neither of these will compile
// DisplayContents(new string("")); //
return 0;
}
so, in the templated method DisplayContents<>(const T& Input) , there is no intellisense on Input. When you type the period character, no suggestions pop up (which isn't that suprising since the function parameter hasn't specified that the input must be a list or any other type of STL container).
However, if you try and send something that isn't an STL container into DisplayContents<>(const T& Input), then the compiler throws these errors:-
error C2100: illegal indirection
error C2228: left of '.cbegin' must have class/struct/union
error C3536: 'iElement': cannot be used before it is initialized
suggesting that the compiler does know something about the type of the parameter needing to have some basic characteristics.
Can anyone please explain how the compiler "knows" that cbegin() and * operator can be used when a list is sent as the parameter, but not when a string or an int is sent, when apparently the type isn't known as intellisense isn't picking up the method cbegin() ?
It's quite simple, really. The compiler will pretend that T is the type of argument you passed in and then proceed with compilation. If it runs into any errors then it will report those. As long as the type of argument you use would work if you hard-coded that type then it will work.
In your case, it fails with int because an int has no cbegin() method.
It fails with new std::string("") because the argument type becomes std::string * const &, and you can't call .cbegin() on this pointer. (You would have to call ->cbegin() instead.)
However, you can call it with std::string("") (note the lack of new) which will cause the argument to be const std::string &, and this will compile.
So it has nothing at all to do with the compiler "knowing that T represents a standard container." If you create a simple type with cbegin() and cend() methods and make sure that the return values from those methods can be incremented, dereferenced, and compared for equality, that type would work just as well.
(Here is a demo of your template function working with a user-defined type.)
Template used to make generic code.
And here the compiler will generate three overload functions for you.
void DisplayContents (const std::list<int>& Input)
void DisplayContents (const int& Input)
void DisplayContents (string* const & Input)
obviously version 2 and 3 won't compile, type const int& and string* const& does not have method cbegin() nor cend()
PS: version 3 param should be string* const& Input, thanks to cdhowie
Consider the following code:
template <typename T>
class B
{
};
template <typename T>
B<T> f(T& t)
{
return B<T>();
}
class A
{
class C {};
C c;
public:
A() {}
decltype(f(c)) get_c() const { return f(c); }
};
int main()
{
A a;
a.get_c();
}
When I try to compile this, I get the error:
test.cpp: In member function 'B<A::C> A::get_c() const':
test.cpp:31:46: error: conversion from 'B<const A::C>' to non-scalar type 'B<A::C>' requested
It seems that in the decltype, the compiler doesn't know that this is a const member function and therefore c is of type const C, and as a result incorrectly deduces the type of f(c) to be B<C> rather than B<const C> which is what it really is.
Am I doing something incorrectly, or is this a compiler bug? I use gcc 4.6, but 4.4 and 4.5 exhibit the same behaviour.
The compiler operates correctly according to the current C++0x WP. See this issue report, which is currently being worked on.
Possibly the final C++0x Standard won't change the meaning of your decltype application in the return type before the function name. You would need to move it to after the parameter list using -> decltype(f(c)), which hopefully will do The Right thing in final C++0x.
No, decltype is not supposed to take into account whether the function is const or not, because it can't. The same could have been written differently:
typedef decltype(f(c)) return_type;
return_type get_c() const { return f(c); }
Correction: decltype(f(c)) shouldn't even compile, because c is not static.
f needs to take an rvalue reference, not an lvalue reference.
I don't think you're allowed to use decltype on anything you wouldn't normally be able to call. I haven't been able to find anything in the standard that would allow you to access c, even within a decltype expression, outside of anywhere you could use c. Since you don't have a this pointer at the point you're trying to do your thing, I don't think you can do what you're trying to do. Doing so doesn't work in MSVC 2010 at least, and it has nothing to do with const.
I considered using declval to get one but you can't access A&&.c because A is an incomplete type at that point. I can't see anyway to do what you're trying to do other than something like so:
decltype(f(declval<C const>())) get_c() const { ... }
I'm having trouble with some valarray function pointer code:
double (*fp)(double) = sin;
valarray<double> (*fp)(const valarray<double> &) = sin;
The first compiles, the second gives:
error: no matches converting function 'sin' to type 'class std::valarray<double> (*)(const class std::valarray<double>&)'
This compiles, using the __typeof__ GCC extension. Looks like GCC's valarray uses expression templates to delay calculation of the sinus. But that will make the return type of the sin template not exactly valarray<T>, but rather some weird complex type.
#include <valarray>
template<typename T> struct id { typedef T type; };
int main() {
using std::valarray;
using std::sin;
id<__typeof__(sin(valarray<double>()))>::type (*fp)(const valarray<double> &) = sin;
}
Edit: See AProgrammer's standard quote for why GCC is fine doing that.
Edit: Standard compliant workaround
Doing this without __typeof__ in a strictly Standard conforming way is a bit tricky. You will need to get the return type of sin. You can use the conditional operator for this, as Eric Niebler has shown. It works by having the sin function not actually called, but only type-checked. By trying to convert the other branch (the one which is actually evaluated) of the conditional operator to that same type, we can generate a dummy parameter just to be able to deduce the type of the function pointer:
#include <valarray>
using std::valarray;
template<typename T> struct id {
typedef T type;
};
struct ded_ty {
template<typename T>
operator id<T>() { return id<T>(); }
};
template<typename E, typename T>
id<T(*)(valarray<E> const&)> genFTy(T t) {
return id<T(*)(valarray<E> const&)>();
}
template<typename T>
void work(T fp, id<T>) {
// T is the function pointer type, fp points
// to the math function.
}
int main() {
work(std::sin, 1 ? ded_ty() : genFTy<double>(std::sin(valarray<double>())));
}
If you want to get the address right away, you can write work so it returns fp again.
template<typename T>
T addy(T fp, id<T>) { return fp; }
Now, you can finally write a macro to encapsulate the conditional operator trickery, and use it when you want to get the address of any such math function.
#define DEDUCE(FN,Y) (1 ? ded_ty() : genFTy<Y>(FN(std::valarray<Y>())))
To get the address and pass it to some generic function, the following works then
std::transform(v1.begin(), v1.end(), v1.begin(),
addy(std::sin, DEDUCE(std::sin, double)));
std::transform(v2.begin(), v2.end(), v2.begin(),
addy(std::cos, DEDUCE(std::cos, double)));
26 3.1/3
Any function returning a valarray is permitted to return an object of another type,
provided all the const member functions of valarray are also applicable to this type.
The aim is to allow template expressions to be used to optimize the result (i.e. looping one time on the whole array doing each times the computation, directly assigning to the resulting valarray<> instead of building a temporary).
z = sin(x+y);
can be optimized to
for (i = 0; i < N; ++i)
z[i] = sin(x[i] + y[i]);
You speak about std::sin in the title, but then assign ::sin.
valarray<double> (*fp)(const valarray<double> &) = std::sin;
That should work. Note, that you should qualify all uses of sin, though most implementations will inject the name to the global namespace even if you include <cmath> (that is non-standard behavior).
Edit: unfortunately, you're out of luck. The standard says about sin(valarray<T> const &) the following (26.3.3.3).
This function shall return a value which is of type T or which can be unambiguously
converted to type T.
Optimizations performed by gcc are granted by the standard. The code above is not guaranteed to work.