Template func and non template func call order - c++

In Linux I get
template max() is called
But under Windows I get
non-template max() is called
Why? In Linux, I use gcc 4.5 and in Windows I use VS2008.
#include <iostream>
#include <vector>
template < typename T >
inline void max( const int& a, const T& b )
{
std::cout << "template max() is called" << std::endl;
}
template < typename T >
inline void Imax( const int& a, const std::vector<T>& b)
{
max(a, b[0]);
}
inline void max( const int& a, const int& b )
{
std::cout << "non-template max() is called" << std::endl;
}
int main()
{
std::vector<int> v;
v.push_back(1);
Imax(1, v);
return 0;
}

In pre-standard C++, you would likely get the non-template max.
(Without a standard, it's hard to say what you should get, but all of
the pre-standard compilers I know would defer name-lookup to
instantiation.) Since C++89, you should get template max; name lookup
occurs in two phases: when the template is defined (at which point, only
the template max is visible), and when the template is instantiated,
but at the instantiation point, only for dependent names, and only using
ADL. In your code, max is a dependent name, but the symbols
triggering ADL are std::vector (which draws in std) and int, which
doesn't add anything, not even the global namespace. So the
non-template max is not found.
These rules were among the last formalized by the C++ committee, and
compilers can't change overnight, so practically speaking, if the
compiler dates from anytime before 1995, you can probably expect the
pre-standard behavior. For anything later, I'd tend to consider it a
compiler error, but... compilers have to support existing code, and
ideally, later compilers will have an option to use the previous name
lookup rules. (I say ideally, because having two incompatible sets of
name lookup rules is decidedly non-trivial. Just getting one set
correct is difficult enough for most compiler implementors.)
And it's fairly well known that Microsoft's implementation of templates
is not standard conformant, even today.

The call of max in Imax depend on T and thus max should be searched in the template definition context (where the template max is) and combined with the argument dependent lookup in the instantiation context. ADL shouldn't find the free standing max as int have no associated namespace. So my take is that gcc is correct.
Note that if you have the slight variation:
#include <iostream>
#include <vector>
struct S {};
template < typename T >
inline void max( const S& a, const T& b )
{
std::cout << "template max() is called" << std::endl;
}
template < typename T >
inline void Imax( const S& a, const std::vector<T>& b)
{
max(a, b[0]);
}
inline void max( const S& a, const S& b )
{
std::cout << "non-template max() is called" << std::endl;
}
int main()
{
std::vector<S> v;
v.push_back(S());
Imax(S(), v);
return 0;
}
here, the global namespace is associated with S, and thus the non template max is found by the ADL lookup at the point of instantiation.

Related

Does passing fundamental values by const reference really hurt performance?

I'm writing a library which does numeric computations. I'm using templates so that the end-user can pick the precision they want. I would like this to work both with fundamental types (double, float) and high precision class types (e.g. boost::multiprecision). I'm wondering then if the argument types should be T or const & T.
On SO/google there are many posts about passing by value vs by reference. One of the "rules-of-thumb" seems to be:
Pass fundamental types by value
Pass everything else by const reference
However, this gets muddy if you have a template:
template<typename T>
T doSomething(T x, T y)
{
return x + y;
}
vs.
template<typename T>
T doSomething(const T & x, const T & y)
{
return x + y;
}
For boost::multiprecision you almost certainly want to pass by const reference. The question is whether passing double by const & is worse than by value. Many SO answers say const & is "no better and maybe worse"...but I couldn't find any good hard references.
I did the following benchmark
which seems to indicate there's no difference although maybe it depends on the simplicity of the function and inlining behavior.
There's the possibility of doing something like:
#include <type_traits>
template<typename T>
using choose_arg_type =
typename std::conditional<std::is_fundamental<T>::value,
T,
const T &>::type;
template <typename T>
T someFunc(choose_arg_type<T> arg)
{
return arg + arg;
}
int main()
{
auto result = someFunc<double>(0.0);
return 0;
}
But if it brings no benefit, it's added complexity and you lose type deduction (Any way to fix this type deduction?)
One reason I can think that pass by const reference is slower is, if it truly is using a reference, there may be cache locality issues. But if the compiler just optimizes to value...this won't matter.
What's the best way to handle this?
There is at least one circumstance where passing by const reference might disable optimizations. However, the most popular compilers provide a way to re-enable them.
Let’s look at this function:
int cryptographicHash( int& salt, const int& plaintext )
{
salt = 4; // Chosen by fair dice roll
// guaranteed to be random
return plaintext; // If we tell them there's a salt,
// this is the last hash function they'll
// ever suspect!
}
Looks pretty secure, right? But, since we’re writing in C++, is it as fast as it could possibly be? (Definitely what we want in a cryptographic hash.)
No, because what if you call it with:
int x = 0xFEED;
const int y = cryptographicHash( x, x );
Now the parameters passed by reference alias the same object, so the function should, as written, return 4, not 0xFEED. This means that, disastrously, the compiler can no longer optimize away the & in its const int& parameter.
However, the most popular compilers (including GCC, clang, Intel C++ and Visual C++ 2015 and up) all support the __restrict extension. So, change the function signature to int cryptographicHash( int& salt, const int& __restrict plaintext ) and all problems with it are solved forever.
Since this extension is not part of the C++ standard, you can improve portability with something like the following:
#if ( __GNUC__ || __clang__ || __INTEL_COMPILER || __ICL || _MSC_VER >= 1900 )
# define RESTRICT __restrict
#else
# define RESTRICT /**/
#endif
int cryptographicHash( int& salt, const int& RESTRICT plaintext );
(In GCC and clang, this does not appear to change the generated code.)
On platforms where the fundamental type in question fits into a register, a decent compiler should eliminate const references from parameters if it can see both sides of the call. For templates that is usually a given (unless they were explicitly instantiated somewhere). Since your library presumably has to be templated all the way down, this will apply to your case.
It's possible that your end users will have bad compilers or platforms where e.g. a double does not fit into a register. I don't see why you'd be incentivized to make micro-optimizations for these particular users, but maybe you do.
It's also possible that you want to explicitly instantiate all templates in your library for some set of types and provide implementation-less header files. In that case the user's compiler must obey whatever calling conventions exist on that platform and will probably pass fundamental types by reference.
Ultimately, the answer is "profile the relevant and representative use cases" if you don't have faith in the compiler(s).
Edit (removed macro solution): As suggested by Jarod42, the C++ way would be using an alias template. This also avoids the lack of deduction that the asker was running into with their original approach:
template<class T>
using CONSTREF = const T&; // Or just T for benchmarking.
https://godbolt.org/z/mopZ6B
As cppreference says:
Alias templates are never deduced by template argument deduction when deducing a template template parameter.
Passing something like int by reference (basically a pointer) is clearly sub-optimal since the extra indirection through the pointer can incur a cache miss and it may also prevent compiler optimizations since the compiler can't always know that the pointed-to variable cannot be changed by other entities, so it may in some cases be forced to do additional loads from memory. Passing by value removes the indirection and lets the compiler assume that noone else is changing the value.
This is a complex question that depends on architecture, compiler optimizations and many other specifics as the variation in answers shows. Since the OP is about writing template functions there is also the option of controlling which function is called by using SFINAE.
#include <iostream>
template <typename T, typename = typename std::enable_if_t<std::is_fundamental_v<T>> >
void f(T t) {
std::cout << "Pass by value\n";
}
template <typename T, typename = typename std::enable_if_t<not std::is_fundamental_v<T>> >
void f(T const &t) {
std::cout << "Pass by const ref.\n";
}
class myclass {};
int main() {
float x;
int i;
myclass c;
std::cout << "float: ";
f(x);
std::cout << "int: ";
f(i);
std::cout << "myclass: ";
f(c);
return 0;
}
Output:
float: Pass by value
int: Pass by value
myclass: Pass by const ref.
If an argument is trivially constructible and isn't modified, pass by value. The calling convention will automatically pass large structs by reference.
struct alignas(4096) page {unsigned char bytes[4096];};
[[nodiscard]] constexpr page operator^(page l, page r) noexcept {
for (int i = 0; i < 4096; ++i)
l.bytes[i] = l.bytes[i] ^ r.bytes[i];
return l;
}
Arguments that are modified and/or returned by nonconst reference must be passed by nonconst reference.
constexpr page& operator^=(page& l, page r) noexcept {return l = l ^ r;}
Pass any argument returned with const reference semantics by const reference.
using buffer = std::vector<unsigned char>;
[[nodiscard]] std::string_view to_string_view(const buffer& b) noexcept {
return {reinterpret_cast<const char*>(b.data()), b.size()};
}
Pass any argument deep copied to a different type by const reference.
[[nodiscard]] std::string to_string(const buffer& b) {
return std::string{to_string_view(b)};
}
Pass any non-trivially constructible, unmodified and non-deep copied argument by const reference.
std::ostream& operator<<(std::ostream& os, const buffer& b) {
os << std::hex;
for (const unsigned short u8 : b)
os << u8 << ',';
return os << std::dec;
}
Pass any argument deep copied to a value of the same type by value. There's no point in passing by reference an argument that's copied anyway, and the constructor for the returned copy is optimized away. See https://en.cppreference.com/w/cpp/language/copy_elision
[[nodiscard]] buffer operator^(buffer l, const buffer& r) {
const auto lsize = l.size();
const auto rsize = r.size();
const auto minsize = std::min(lsize, rsize);
for (buffer::size_type i = 0; i < minsize; ++i)
l[i] = l[i] ^ r[i];
if (lsize < rsize)
l.insert(l.end(), r.begin() + minsize, r.end());
return l;
}
This includes template functions as well.
template<typename T>
[[nodiscard]]
constexpr T clone(T t) noexcept(std::is_nothrow_constructible_v<T, T>) {
return t;
}
Otherwise take arguments of template parameter type by forwarding reference (&&). Note: && only has forwarding (universal) reference semantics in arguments of template parameter type, and/or for auto&& or decltype(auto)&&.
template<typename T>
constexpr bool nt = noexcept(std::is_nothrow_constructible_v<int, T&&>);
template<typename T>
[[nodiscard]]
constexpr int to_int(T&& t) noexcept(nt<T>) {return static_cast<int>(t);}
const auto to_int_lambda = [](auto&& t) noexcept(noexcept(to_int(t))) {
return to_int(t);
};

C++ noexcept declaration changes template deduction

I was tinkering to confirm the example on page 91 of Effective Modern C++, and I ran into what seems to be a strange issue. This code
template<typename C>
void doStuff(C& a, C& b) noexcept(noexcept(doStuff(a.front(), b.front()))) {
std::cout << "container version" << std::endl;
}
template<>
void doStuff<int>(int& x, int& y) noexcept {
std::cout << "int version" << std::endl;
}
int main() {
vector<int> v1 = {1, 2, 3};
vector<int> v2 = {4, 5, 6};
int x = 5;
int y = 6;
doStuff(x, y);
doStuff(v1, v2);
}
Gives me an error like
error: request for member ‘front’ in ‘a’, which is of non-class type
‘int’ void doStuff(C& a, C& b) noexcept(noexcept(doStuff(a.front(),
b.front()))) {
So, it seems like the top version of doStuff is being called, even though a.front() and b.front() should be returning references to ints. If I remove all the noexcept declarations from the code, I get the expected output.
This is with gcc 5.4.
What am I doing wrong?
Thanks
The problem is, when the name lookup at this point:
template<typename C>
void doStuff(C& a, C& b) noexcept(noexcept(doStuff(a.front(), b.front()))) {
// ^^^^^^^
will just find one doStuff(): your function template. The specialization hasn't been declared yet, so it isn't considered.
First thing to do is to simply avoid specializations. They're awkward. But then the real fix would be to stick in an extra empty type solely for argument-dependent lookup purposes. This will add a dependent name to the noexcept lookup that will delay invocation until instantiation:
namespace N {
struct adl { };
void doStuff(adl, int& , int& ) noexcept {
std::cout << "int version" << std::endl;
}
template<typename C>
void doStuff(adl, C& a, C& b) noexcept(noexcept(doStuff(adl{}, a.front(), b.front()))) {
std::cout << "container version" << std::endl;
}
}
template <class C>
void doStuff(C& a, C& b) noexcept(noexcept(doStuff(N::adl{}, a, b)))
{
doStuff(N::adl{}, a, b);
}
Template specializations are not overloads. Your specialization for doStuff<int> is not an overload of doStuff<C>, it is a specialization. So overload resolution doesn't consider it, template instantiation will consider it, if the original is selected by overload resolution. Replace your specialization with an overload (non-template, taking two int&s)
void doStuff(int& a, int& b) noexcept;

GCC 4.9 ambiguous overload template specialization

I'm running into an issue with gcc 4.9.2 (with -std=c++11) not compiling a piece of code with the error message being
call of overloaded 'InsertDataIntoInputMap(int&, boost::shared_ptr&)' is ambiguous
The code does compile with msvc 2013
#include <iostream>
#include <map>
#include <boost/shared_ptr.hpp>
struct Proxy
{
typedef std::map<int, int> InputDataMap;
int a;
};
template<class C, class D>
void InsertDataIntoInputMap(
const typename C::InputDataMap::key_type& key,
const D val)
{
std::cout << "Not shared\n";
}
template<class C, class D>
void InsertDataIntoInputMap(
const typename C::InputDataMap::key_type& key,
const boost::shared_ptr<D> val)
{
if (val)
{
std::cout << "Shared\n";
}
}
int main() {
int a;
boost::shared_ptr<double> x(new double(4.5));
InsertDataIntoInputMap<Proxy>(a, x);
}
while the following does actually compile with both gcc and msvc:
#include <iostream>
#include <boost/shared_ptr.hpp>
template<class C, class D>
void InsertDataIntoInputMap(
const C& key,
const D val)
{
std::cout << "Not shared\n";
}
template<class C, class D>
void InsertDataIntoInputMap(
const C& key,
const boost::shared_ptr<D> val)
{
if (val)
{
std::cout << "Shared\n";
}
}
int main() {
int a = 0;
boost::shared_ptr<double> x(new double(4.5));
InsertDataIntoInputMap<int>(a, x);
return 0;
}
I would have thought that the compiler should take the function with the boost::shared_ptr argument in both cases?
This problem can be reduced to an imprecision in partial ordering: Pairs in which no template-parameters appear that are involved in deduction are still considered and compaired. That issue was addressed by CWG #455 and #885 as well.
In your example, overload resolution isn't able to distinguish the overloads. Hence partial ordering is necessary. And partial ordering will try to perform deduction twice, with the parameter type P being typename C::InputDataMap::key_type.
However, that deduction is doomed to fail, since C solely appears in a non-deduced context. I.e. the type from both templates (for that particular pair) is not at least as specialized as the type from the respective other template - and that, in turn, implies that neither of the templates is more specialized than the other.
As noted by #T.C., the resolution of CWG #1391 helps. This part in particular:
Change 14.8.2.4 [temp.deduct.partial] paragraph 4 as follows:
Each type nominated above from the parameter template and the corresponding type from the argument template are used as the types of P and A. If a particular P contains no template-parameters that participate in template argument deduction, that P is not used to determine the ordering.
Now, the first parameter pair is entirely ignored in both ways (as the types of C are solely determined from the explicit argument list), and the second overload is found to be more specialized.
A simple alias can make that code work:
#include <iostream>
#include <map>
#include <boost/shared_ptr.hpp>
struct Proxy
{
typedef std::map<int, int> InputDataMap;
int a;
};
template<class C, class D, class F = typename C::InputDataMap::key_type>
void InsertDataIntoInputMap(
const F& key,
const D val)
{
std::cout << "Not shared\n";
}
template<class C, class D, class F = typename C::InputDataMap::key_type>
void InsertDataIntoInputMap(
const F& key,
const boost::shared_ptr<D> val)
{
if (val)
{
std::cout << "Shared\n";
}
}
int main() {
int a;
boost::shared_ptr<double> x(new double(4.5));
InsertDataIntoInputMap<Proxy>(a, x);
}
But imo. this shouldn't work, cause I thought, the draft says, the compiler will not consider the C::InputDataMap - Namespace in
class F = typename C::InputDataMap::key_type
and F will be a non-deduced context (like key_type).

Why the template version is chosen below by the compiler?

The template version is used by the compiler to calculate t = max(a, b) and max(t, c). Any quote from the Standard supporting this is welcome.
#include <iostream>
template <typename T>
inline T const& max (T const& a, T const& b)
{
std::cout << "template" << '\n';
return a < b ? b : a;
}
template <typename T>
inline T const& max (T const& a, T const& b, T const& c)
{
return max (max(a,b), c);
}
inline int const& max (int const& a, int const& b)
{
std::cout << "non-template" << '\n';
return a <b ? b : a;
}
int main()
{
std::cout << max(3, 5, 7) << '\n';
}
The code prints
template
template
7
The definition of your non-template version of max() is not visible at the call-site, it's defined afterwards. Either move that function above the 3-argument max() or add a prototype above the call-site.
int const& max (int const& a, int const& b);
Now the non-template version is chosen in both cases.
Live example
As for why this is the case, I believe §3.4.1/1 [basic.lookup.unqual] holds the answer.
In all the cases listed in 3.4.1, the scopes are searched for a declaration in the order listed in each of the respective categories; name lookup ends as soon as a declaration is found for the name. If no declaration is found, the program is ill-formed.
Note that argument-dependent name lookup does not apply in your case since the arguments to max are int, and not a user defined type. Only unqualified name lookup applies, hence, as quoted above, the lookup stops when the first match (the function template version of max()) is found.
The last sentence in the quoted section also explains why if you comment out the function template version of max() your code will not compile.

Function passed as template argument

I'm looking for the rules involving passing C++ templates functions as arguments.
This is supported by C++ as shown by an example here:
void add1(int &v) { v += 1 }
void add2(int &v) { v += 2 }
template <void (*T)(int &)>
void doOperation()
{
int temp = 0;
T(temp);
std::cout << "Result is " << temp << std::endl;
}
int main()
{
doOperation<add1>();
doOperation<add2>();
}
Learning about this technique is difficult, however. Googling for "function as a template argument" doesn't lead to much. And the classic C++ Templates The Complete Guide surprisingly also doesn't discuss it (at least not from my search).
The questions I have are whether this is valid C++ (or just some widely supported extension).
Also, is there a way to allow a functor with the same signature to be used interchangeably with explicit functions during this kind of template invocation?
The following does not work in the above program, at least in Visual C++, because the syntax is obviously wrong. It'd be nice to be able to switch out a function for a functor and vice versa, similar to the way you can pass a function pointer or functor to the std::sort algorithm if you want to define a custom comparison operation.
struct add3 {
void operator() (int &v) {v += 3;}
};
...
doOperation<add3>();
Pointers to a web link or two, or a page in the C++ Templates book would be appreciated!
Yes, it is valid.
As for making it work with functors as well, the usual solution is something like this instead:
template <typename F>
void doOperation(F f)
{
int temp = 0;
f(temp);
std::cout << "Result is " << temp << std::endl;
}
which can now be called as either:
doOperation(add2);
doOperation(add3());
See it live
The problem with this is that if it makes it tricky for the compiler to inline the call to add2, since all the compiler knows is that a function pointer type void (*)(int &) is being passed to doOperation. (But add3, being a functor, can be inlined easily. Here, the compiler knows that an object of type add3 is passed to the function, which means that the function to call is add3::operator(), and not just some unknown function pointer.)
Template parameters can be either parameterized by type (typename T) or by value (int X).
The "traditional" C++ way of templating a piece of code is to use a functor - that is, the code is in an object, and the object thus gives the code unique type.
When working with traditional functions, this technique doesn't work well, because a change in type doesn't indicate a specific function - rather it specifies only the signature of many possible functions. So:
template<typename OP>
int do_op(int a, int b, OP op)
{
return op(a,b);
}
int add(int a, int b) { return a + b; }
...
int c = do_op(4,5,add);
Isn't equivalent to the functor case. In this example, do_op is instantiated for all function pointers whose signature is int X (int, int). The compiler would have to be pretty aggressive to fully inline this case. (I wouldn't rule it out though, as compiler optimization has gotten pretty advanced.)
One way to tell that this code doesn't quite do what we want is:
int (* func_ptr)(int, int) = add;
int c = do_op(4,5,func_ptr);
is still legal, and clearly this is not getting inlined. To get full inlining, we need to template by value, so the function is fully available in the template.
typedef int(*binary_int_op)(int, int); // signature for all valid template params
template<binary_int_op op>
int do_op(int a, int b)
{
return op(a,b);
}
int add(int a, int b) { return a + b; }
...
int c = do_op<add>(4,5);
In this case, each instantiated version of do_op is instantiated with a specific function already available. Thus we expect the code for do_op to look a lot like "return a + b". (Lisp programmers, stop your smirking!)
We can also confirm that this is closer to what we want because this:
int (* func_ptr)(int,int) = add;
int c = do_op<func_ptr>(4,5);
will fail to compile. GCC says: "error: 'func_ptr' cannot appear in a constant-expression. In other words, I can't fully expand do_op because you haven't given me enough info at compiler time to know what our op is.
So if the second example is really fully inlining our op, and the first is not, what good is the template? What is it doing? The answer is: type coercion. This riff on the first example will work:
template<typename OP>
int do_op(int a, int b, OP op) { return op(a,b); }
float fadd(float a, float b) { return a+b; }
...
int c = do_op(4,5,fadd);
That example will work! (I am not suggesting it is good C++ but...) What has happened is do_op has been templated around the signatures of the various functions, and each separate instantiation will write different type coercion code. So the instantiated code for do_op with fadd looks something like:
convert a and b from int to float.
call the function ptr op with float a and float b.
convert the result back to int and return it.
By comparison, our by-value case requires an exact match on the function arguments.
Function pointers can be passed as template parameters, and this is part of standard C++
. However in the template they are declared and used as functions rather than pointer-to-function. At template instantiation one passes the address of the function rather than just the name.
For example:
int i;
void add1(int& i) { i += 1; }
template<void op(int&)>
void do_op_fn_ptr_tpl(int& i) { op(i); }
i = 0;
do_op_fn_ptr_tpl<&add1>(i);
If you want to pass a functor type as a template argument:
struct add2_t {
void operator()(int& i) { i += 2; }
};
template<typename op>
void do_op_fntr_tpl(int& i) {
op o;
o(i);
}
i = 0;
do_op_fntr_tpl<add2_t>(i);
Several answers pass a functor instance as an argument:
template<typename op>
void do_op_fntr_arg(int& i, op o) { o(i); }
i = 0;
add2_t add2;
// This has the advantage of looking identical whether
// you pass a functor or a free function:
do_op_fntr_arg(i, add1);
do_op_fntr_arg(i, add2);
The closest you can get to this uniform appearance with a template argument is to define do_op twice- once with a non-type parameter and once with a type parameter.
// non-type (function pointer) template parameter
template<void op(int&)>
void do_op(int& i) { op(i); }
// type (functor class) template parameter
template<typename op>
void do_op(int& i) {
op o;
o(i);
}
i = 0;
do_op<&add1>(i); // still need address-of operator in the function pointer case.
do_op<add2_t>(i);
Honestly, I really expected this not to compile, but it worked for me with gcc-4.8 and Visual Studio 2013.
In your template
template <void (*T)(int &)>
void doOperation()
The parameter T is a non-type template parameter. This means that the behaviour of the template function changes with the value of the parameter (which must be fixed at compile time, which function pointer constants are).
If you want somthing that works with both function objects and function parameters you need a typed template. When you do this, though, you also need to provide an object instance (either function object instance or a function pointer) to the function at run time.
template <class T>
void doOperation(T t)
{
int temp=0;
t(temp);
std::cout << "Result is " << temp << std::endl;
}
There are some minor performance considerations. This new version may be less efficient with function pointer arguments as the particular function pointer is only derefenced and called at run time whereas your function pointer template can be optimized (possibly the function call inlined) based on the particular function pointer used. Function objects can often be very efficiently expanded with the typed template, though as the particular operator() is completely determined by the type of the function object.
The reason your functor example does not work is that you need an instance to invoke the operator().
Came here with the additional requirement, that also parameter/return types should vary.
Following Ben Supnik this would be for some type T
typedef T(*binary_T_op)(T, T);
instead of
typedef int(*binary_int_op)(int, int);
The solution here is to put the function type definition and the function template into a surrounding struct template.
template <typename T> struct BinOp
{
typedef T(*binary_T_op )(T, T); // signature for all valid template params
template<binary_T_op op>
T do_op(T a, T b)
{
return op(a,b);
}
};
double mulDouble(double a, double b)
{
return a * b;
}
BinOp<double> doubleBinOp;
double res = doubleBinOp.do_op<&mulDouble>(4, 5);
Alternatively BinOp could be a class with static method template do_op(...), then called as
double res = BinOp<double>::do_op<&mulDouble>(4, 5);
EDIT
Inspired by comment from 0x2207, here is a functor taking any function with two parameters and convertible values.
struct BinOp
{
template <typename R, typename S, typename T, typename U, typename V> R operator()(R (*binaryOp )(S, T), U u, V v)
{
return binaryOp(u,v);
}
};
double subD(double a, int b)
{
return a-b;
}
int subI(double a, int b)
{
return (int)(a-b);
}
int main()
{
double resD = BinOp()(&subD, 4.03, 3);
int resI = BinOp()(&subI, 4.03, 3);
std::cout << resD << std::endl;
std::cout << resI << std::endl;
return 0;
}
correctly evaluates to double 1.03 and int 1
Edit: Passing the operator as a reference doesnt work. For simplicity, understand it as a function pointer. You just send the pointer, not a reference.
I think you are trying to write something like this.
struct Square
{
double operator()(double number) { return number * number; }
};
template <class Function>
double integrate(Function f, double a, double b, unsigned int intervals)
{
double delta = (b - a) / intervals, sum = 0.0;
while(a < b)
{
sum += f(a) * delta;
a += delta;
}
return sum;
}
.
.
std::cout << "interval : " << i << tab << tab << "intgeration = "
<< integrate(Square(), 0.0, 1.0, 10) << std::endl;