1

I want to define a vector space (i.e. component-wise addition and multiplication by a scalar) over structs with various components. So in this short example I overload operator*= for the specific struct and then use this in templates for any operator*.

The following code compiles and runs:

#include <assert.h>


struct myfloat3 { float x, y, z; };


myfloat3 operator*=(myfloat3& a, const float b) {
    a.x *= b; a.y *= b; a.z *= b;
    return a;
}

template<typename Pt>
Pt operator*(const Pt& a, const float b) {
    auto prod = a;
    prod *= b;
    return prod;
}

// template<typename Pt>
// Pt operator*(const float b, const Pt& a) {
//     auto prod = a;
//     prod *= b;
//     return prod;
// }


int main(int argc, char const *argv[]) {
    myfloat3 x {1, 2, 3};

    assert((x*3.0f).x == 3);

    return 0;
}

But if I uncomment the second overload for the other ordering, float * myfloat3, I get

$ g++ -std=c++11 sandbox/overload.cpp
sandbox/overload.cpp:20:4: error: overloaded 'operator*' must have at least one
      parameter of class or enumeration type
Pt operator*(const float b, const Pt& a) {
   ^
sandbox/overload.cpp:30:14: note: in instantiation of function template
      specialization 'operator*<float>' requested here
    assert((x*3.0f).x == 3);
             ^
/usr/include/assert.h:93:25: note: expanded from macro 'assert'
    (__builtin_expect(!(e), 0) ? __assert_rtn(__func__, __FILE__, __LINE...
                        ^
1 error generated.

$ g++ --version
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/usr/include/c++/4.2.1
Apple LLVM version 6.0 (clang-600.0.56) (based on LLVM 3.5svn)
Target: x86_64-apple-darwin13.4.0
Thread model: posix

However using a different compiler it works:

$ g++ --version
g++ (Ubuntu 4.9.2-10ubuntu13) 4.9.2
$ g++ -std=c++11 sandbox/soverload.cpp

Any ideas?

germannp
  • 197
  • 4
  • 15
  • 1
    https://stackoverflow.com/q/18596412/4326278 is relevant, but note the comments to the accepted answer. – bogdan Jul 25 '16 at 01:38
  • Thank you, I will try this SFINA magic when I am back on my laptop with the complaining compiler. – germannp Jul 25 '16 at 09:32

1 Answers1

0

Use a trait class with enable_if to control when the template is enabled:

template<typename Pt> struct Is_vector: public std::false_type {};
template<> struct Is_vector<float3>: public std::true_type {};

template<typename Pt>
typename std::enable_if<Is_vector<Pt>::value, Pt>::type 
operator*(const Pt& a, const float b) {
    auto prod = a;
    prod *= b;
    return prod;
}

template<typename Pt>
typename std::enable_if<Is_vector<Pt>::value, Pt>::type 
operator*(const float b, const Pt& a) {
    auto prod = a;
    prod *= b;
    return prod;
}

See https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/enable-if and Thrust reduction and overloaded operator-(const float3&, const float3&) won't compile for explanations.

germannp
  • 197
  • 4
  • 15