4

I'm getting a strange compiler error from g++.

It says "std::plus is not a function" for the following code, even though I'm not including <functional> and I'm not using std where the error occurs.

Here is the code:

#include <iostream>

template<class T1, class T2, class T3>
struct MyStruct {
  T1 t1; T2 t2; T3 t3;
  MyStruct() {}
  MyStruct(T1 const& t1_, T2 const& t2_, T3 const& t3_)
    : t1(t1_), t2(t2_), t3(t3_) {}
};

template<class T1, class T2, class T3>
MyStruct<T1, T2, T3> plus(MyStruct<T1, T2, T3> const& x,
              MyStruct<T1, T2, T3> const& y) {
  // ...
}

int main() {
  typedef MyStruct<int, double, std::string> Struct;
  Struct x(2, 5.6, "bar");
  Struct y(6, 4.1, "foo");
  Struct result = plus(x, y);
}

Here is the full error (slightly reformatted):

/usr/include/c++/4.2.1/bits/stl_function.h: In function 'int main()':
/usr/include/c++/4.2.1/bits/stl_function.h:134:
    error: 'template<class _Tp> struct std::plus' is not a function,
plus3.cc:13: error:
   conflict with 'template<class T1, class T2, class T3> 
     MyStruct<T1, T2, T3> plus(const MyStruct<T1, T2, T3>&, 
       const MyStruct<T1, T2, T3>&)'
plus3.cc:21: error:   in call to 'plus'

Does anyone know why this is and how to avoid the error? I'd really like to call the function plus.

The error doesn't occur when my plus function doesn't have 3 template arguments, which makes some sense after looking at the definition of std::plus:

  template <class _Tp>
    struct plus : public binary_function<_Tp, _Tp, _Tp>

But it's still strange because std::plus shouldn't even be known at that point.

UPDATE:

In response to some answers, I'll paste slightly modified code which also gives the error. My plus function is in namespace foo here, and it is called from the same namespace, so there shouldn't be any need to qualify it using foo:::

#include <string>

namespace foo {

template<class T1, class T2, class T3>
struct MyStruct {
  T1 t1; T2 t2; T3 t3;
  MyStruct() {}
  MyStruct(T1 const& t1_, T2 const& t2_, T3 const& t3_)
      : t1(t1_), t2(t2_), t3(t3_) {}
};

template<class T1, class T2, class T3>
MyStruct<T1, T2, T3> plus(MyStruct<T1, T2, T3> const& x,
                          MyStruct<T1, T2, T3> const& y) {
  // ...                                                                                                                        
}

template<class T1, class T2, class T3>
MyStruct<T1, T2, T3> bar(MyStruct<T1, T2, T3> const& x,
                         MyStruct<T1, T2, T3> const& y) {
  return plus(x, y);
}

} // end foo                                                                                                                    

int main() {
  typedef foo::MyStruct<int, double, std::string> Struct;
  Struct x(2, 5.6, "bar");
  Struct y(6, 4.1, "foo");
  Struct result = foo::bar(x, y);
}
Frank
  • 64,140
  • 93
  • 237
  • 324
  • 1
    This compiles with gcc 4.5.1 (after commenting out the initializer list) – jrok May 22 '12 at 20:37
  • @jrok: Interesting, so it's maybe a g++ bug. I tried with g++ 4.4.4 as well and am getting the same error. – Frank May 22 '12 at 20:40
  • I do get the same error here with g++ 4.3.2, so it's not just you. And when I insert `using namespace std;` just for the heck of it, only then does it start complaining that `reference to ‘plus’ is ambiguous`. So it does know that, at least! Interesting problem. But it could be just a simple compiler bug, as jrok's comment suggests. – Mr Lister May 22 '12 at 20:42
  • 3
    Your problem might be related with this: [What are the pitfalls of ADL?](http://stackoverflow.com/questions/2958648/what-are-the-pitfalls-of-adl). If you open header of your implementation, you'll see it includes `bits/stl_function.h` (probably they use std::less for string's operator< ) – jrok May 22 '12 at 20:43
  • As jrok mentions, in C++ a standard header is allowed to bring in stuff from other standard headers. This is different than C, where one header is not permitted to bring in declarations from other standard headers. – Michael Burr May 22 '12 at 20:53
  • I get no errors with the version that has `namespace foo` (compiled with gcc 4.2, gcc 4.8, and clang 3.1) and it runs as expected. – Bill Weinman May 22 '12 at 23:25
  • I get a compiler error with the `namespace foo` version with gcc 4.2.1 on Mac OS and with gcc 4.4.4 on Linux. – Frank May 23 '12 at 15:45

3 Answers3

5

It's because std::plus is not a function.

Your function plus is in the global namespace so you need to do

Struct result = ::plus(x, y);

The reason it is trying to use std::plus is because you were not explicit in the version of plus you wanted so the compiler used Koenig lookup which finds plus via its arguments.

Martin York
  • 257,169
  • 86
  • 333
  • 562
  • 1
    Thanks for your answer. It is still a mystery to me, though, because I still get the error if my `plus` is in its own namespace, see the UPDATE that I made in my post. It seems that I'd need to qualify the `plus` call with `foo::plus` even though I'm already in `foo` namespace. Also the 2 arguments to `plus` are in `foo` namespace, not in `std` namespace. – Frank May 22 '12 at 20:49
  • Hm, and is there a way to suppress Koenig Lookup? – Mr Lister May 22 '12 at 20:49
  • Also `Struct result = plus(x, y);` works. – Martin York May 22 '12 at 20:57
  • Okay, but I still don't see a reason why it should be required. – Frank May 22 '12 at 21:09
  • @MrLister: Yes. Be explicit about which function/type you want. – Martin York May 22 '12 at 21:09
0

Try specifying the namespace for you plus() explicitly

Struct result = ::plus(x, y);

This helps with gcc and it doesn't make you put your plus() into different namespace.

Loki Astari tells the rest

Viktor Latypov
  • 14,289
  • 3
  • 40
  • 55
0

You won't get this error if you put your function in a different namespace. This is what namespaces are for -- you can have the same symbol/signature in different namespaces without causing collisions.

Bill Weinman
  • 2,036
  • 2
  • 17
  • 12