3

Here's a compilable sample I stitched together from several header files. The code won't make sense because I gutted all the irrelevant parts, but the gist is that I'm implementing Scott Meyers' data proxy technique (mentioned here), though it's evolved into more of a wrapper than a temporary proxy. None of that should matter though—my question seems to be purely regarding a difference in compiler behaviors.

#include <iostream>
#include <vector>

template<typename T>
class Proxy
{
public:
    enum class State
    {
        NEVER_SET = 0,
        SET,
        UNSET
    };
    operator const T& () const
    {
        if ( _state != State::SET )
        {
            std::cout << "EXCEPTION" << std::endl;
            // TODO throw exception
        }
        return _data;
    }
    Proxy<T>& operator=(const T& val)
    {
        _data = val;
        _state = State::SET;
        return (*this);
    }
    Proxy<T>& operator+=(const T& val)
    {
        _data = (*this) + val;
        _state = State::SET;
        return (*this);
    }
private:
    T _data;
    State _state = State::NEVER_SET;
};

class Tape
{
};

template<typename T>
class tape : public Tape
{
public:
    const Proxy<T>& operator[](int idx) const
    {
        return operator[](idx);
    }
    Proxy<T>& operator[](int idx)
    {
        if ( idx >= data.size() )
        {
            data.resize(idx + 1);
        }
        return data[idx];
    }
private:
    std::vector< Proxy<T> > data;
};

class CRIXUS
{
public:
    virtual void Go() final {};
protected:
    virtual void Pre() {};
    virtual void Post() {};
    virtual void Step() = 0;
};

class CRIXUS_MA : public CRIXUS
{
public:
    int window = 30;
    tape<double> input;
    tape<double> output;
protected:
    virtual void Step()
    {
        double sum = 0;
        for ( int j = 0; j < window; j++ )
        {
            sum += input[-j];
        }
        output[0] = sum / window;
    }
};

int main()
{
}

It compiles fine on Ideone as well as via Jetbrain's CLion (Toolchain: MinGW 3.20, CMake 2.8.12.2):

enter image description here

However it won't compile on VS Express 2013:

enter image description here

Running the full code from CLion (which involves reading a .csv file of numbers and outputting a moving average), I can verify that the output is correct. It's just that VS won't compile the code.

As far as I can tell, the cast operator

    operator const T& () const
    {
        if ( _state != State::SET )
        {
            std::cout << "EXCEPTION" << std::endl;
            // TODO throw exception
        }
        return _data;
    }

should convert the Proxy<T> to T, i.e. Proxy<double> to double. And when I forcibly cast the offending line,

        sum += (double)input[-j];

it works fine. Any ideas?

Community
  • 1
  • 1
slackwing
  • 25,894
  • 12
  • 72
  • 124
  • Which line is line 86? – M.M Sep 02 '14 at 05:58
  • Agh, sorry. It's the `sum += input[-j];`. There, `input[-j]` should be returning a `Proxy`, but since `sum` is `double`, I'd expect a conversion to take place. – slackwing Sep 02 '14 at 05:59
  • Can you reduce to a minimal example? (take out as much as you can but still have the problem occur) – M.M Sep 02 '14 at 05:59
  • I've already taken out 80% of the code :/ but I'll give it a shot. – slackwing Sep 02 '14 at 06:00
  • Your operators are member functions. Make them global. The first operand is a `double`, so that line would be invoked as `sum.operator+(input[-j])`. It's probably compiling elswhere because you never create any of these things, so the templates are simply not compiled at all. Don't know my VC++ does though. – Ed S. Sep 02 '14 at 06:04
  • @EdS. - You're definitely onto something. Let me read up on members versus global operators and get back to you. Only thing I'll disagree on is that the templates aren't being compiled at all—my full code implemented those templates and produced expected results. – slackwing Sep 02 '14 at 06:07
  • 1
    @AndrewCheong: Found this: http://stackoverflow.com/questions/4622330/operator-overloading-member-function-vs-non-member-function – Ed S. Sep 02 '14 at 06:07
  • 1
    Apparently MSVC refuses to instantiate `Proxy` in this code. Adding a `Proxy p;` right before the definition of `CRIXUS_MA` forces the instantiation, and caused the code to compile. – T.C. Sep 02 '14 at 06:12
  • 1
    @EdS. Actually, overload resolution in this case would use the built-in operator and perform a conversion from `Proxy` to `double`. – T.C. Sep 02 '14 at 06:29
  • @T.C.: Ahh, I had a feeling I screwed that up a bit. – Ed S. Sep 02 '14 at 07:40

1 Answers1

4

This appears to be more MSVC template brokenness. It refuses to instantiate Proxy<double> in this code, causing overload resolution to fail. Simply adding a Proxy<double> p; right before the definition of CRIXUS_MA, which forces an implicit instantiation, is sufficient to make the code compile. According to §14.7.1 [temp.inst]/p6:

A class template specialization is implicitly instantiated if the class type is used in a context that requires a completely-defined object type or if the completeness of the class type might affect the semantics of the program. [ Note: In particular, if the semantics of an expression depend on the member or base class lists of a class template specialization, the class template specialization is implicitly generated. For instance, deleting a pointer to class type depends on whether or not the class declares a destructor, and conversion between pointer to class types depends on the inheritance relationship between the two classes involved. —end note ]

Since the semantics of sum += input[-j]; obviously depends on the definition of Proxy<double>, it should have been implicitly instantiated.

T.C.
  • 123,516
  • 14
  • 264
  • 384
  • Thanks; I was just about to post that based on your suggestion, I added `Proxy dummy` as a member to `class tape`, and that worked. (I didn't want to have to specify `double` and other primitives, manually.) The comments regarding member vs. non-member operators seemed really compelling, though. Do you think they were relevant to this issue? Or only seemingly. (I ask you because I don't feel experienced enough to judge.) – slackwing Sep 02 '14 at 06:26
  • 1
    @AndrewCheong That's not relevant to the issue you are having. Plenty of standard library types define `operator +=` as class members. – T.C. Sep 02 '14 at 06:29