Why does using this C++ function twice in one line

2020-07-02 10:49发布

问题:

I ran into some trouble trying to implement a smart equality test macro-type template function in Visual C++ 2010 that had to do with a bug in VS in regard to default arguments of template functions. I fixed it by wrapping the value of the parameter in an extra function, but now I found that I can't use the function twice in one line!

Header file:

// example.h
#pragma once

#include <limits>

namespace myspace
{

// Need to define this separately to avoid a Visual Studio bug
template<typename T> T epsilon() { return std::numeric_limits<T>::epsilon(); }

// A generic equality test
template<typename T> inline bool smartEqual(
    const T &v1, 
    const T &v2, 
    const T &eps = epsilon<T>())
{
    return (v1 == v2);
}

// Template specialization for floating-point numbers
template<> bool smartEqual<float>(
    const float &v1, 
    const float &v2, 
    const float &eps);

} // namespace myspace

Source file:

// example.cpp
#include "example.h"

using namespace std;
using namespace myspace;

// equal-macro specialization for floats using epsilon
template<> bool myspace::smartEqual<float>(
    const float &v1, 
    const float &v2, 
    const float &eps)
{
    return (fabs(v1 - v2) < eps);
}

int _tmain(int argc, _TCHAR* argv[])
{
    float a,b;
    bool x = smartEqual(a,b); // works ok
    bool x = smartEqual(a,b) && smartEqual(b,a); // error
    return 0;
}

The error is reported as follows:

------ Build started: Project: test, Configuration: Debug Win32 ------
test.cpp
c:\users\ninja\documents\visual studio 2010\projects\test\test\test.cpp(24): error C2440: 'default argument' : cannot convert from 'const float *' to 'const float &'
Reason: cannot convert from 'const float *' to 'const float'
There is no context in which this conversion is possible

The offending line is the one where I try to call smartEqual() twice using the logical AND.

I don't understand why this happens. Changing "eps" from a reference type to a straightforward value type fixes it, but I wish I knew what was going on.

Thanks!

回答1:

I think you've now hit this VS10 bug.

Your code compiles OK on VS11 Beta.

You could possibly avoid the default value (which seems to be a major issue for VS10) by changing smartEqual to:

template<typename T> inline bool smartEqual(
    const T &v1, 
    const T &v2)
{
    return (v1 == v2);
}

and simply specialising for float (and double) like this:

template<> bool myspace::smartEqual<float>(
    const float &v1, 
    const float &v2)
{
    return (fabs(v1 - v2) < std::numeric_limits<float>::epsilon());
}


Another option is to change the epsilon parameter to pass by value:

template<typename T> inline bool smartEqual(
    const T &v1, 
    const T &v2, 
    T eps = epsilon<T>())
{
    return (v1 == v2);
}


回答2:

code failed in VS2010 but OK in Intel compiler. looks like a bug in VS2010



回答3:

After some consideration, I've decided to go with a still another solution that @Fraser suggested (although I got the inspiration from him) and write my own answer:

  1. The first solution robs me of the flexibility of being able to use a custom value of eps.
  2. The second solution with the pass-by-value feels wrong, especially if in the future I will decide to use this function for some more contrived types.

Since VS seems overriden with bugs in regard to default values of parameters (only in templates?), it seems the most sensible thing to do is sidestep the issue by creating two versions of smartEqual; with and without the eps (using the default), which pretty much does the same thing, if not as concisely:

// An equality test that doesn't require the value of eps, default will be used
template<typename T> inline bool smartEqual(
    const T &v1, 
    const T &v2)
{
    return (v1 == v2);
}

// Float specialization: return (fabs(v1 - v2) < std::numeric_limits<float>::epsilon());
template<> inline bool smartEqual<float>(
    const float &v1, 
    const float &v2);

// A custom-eps value equality test
template<typename T> inline bool smartEqual(
    const T &v1, 
    const T &v2, 
    const T &eps)
{
    return (v1 == v2);
}

// Float specialization: return (fabs(v1 - v2) < eps);
template<> bool smartEqual<float>(
    const float &v1, 
    const float &v2, 
    const float &eps);