4

I have a container and want to rely on whoever uses my library to make sure that a function is available for the underlying value_type (pow() in the coming example). And I want the compiler to use that function inside of a member function with the same name, based on its signature.

My attempt to create a minimal example:

#include <iostream>
#include <cmath>

using std::pow;

template <typename T>
struct container {
    T value;

    container<T> pow(T const exp) const {
        return {pow(this->value, exp)};
    }
};

int main() {
    container<double> value{81.};
    std::cout << value.value << "^0.25 = " << value.pow(.25).value << '\n';
    return 0;
}

The container<> offers a pow() method, that is supposed to rely on pow() being available from the underlying type in the global namespace.

This is supposed to facilitate the use of custom number-like types. I.e. library users should be able to define their own types that act like numbers and supply a pow() function for their type, to make it container<> compatible.

The problem, neither clang nor gcc pick up the function from the global namespace:

c++ -std=c++11 pow.cpp -o pow
pow.cpp:11:28: error: too many arguments to function call, expected single argument 'exp', have 2 arguments
                return {pow(this->value, exp)};
                        ~~~              ^~~
pow.cpp:17:50: note: in instantiation of member function 'container<double>::pow' requested here
        std::cout << value.value << "^0.25 = " << value.pow(.25).value << '\n';
                                                        ^
pow.cpp:10:2: note: 'pow' declared here
        container<T> pow(T const exp) const {
        ^

If I use the global namespace explicitly, it works as expected:

container<T> pow(T const exp) const {
    return {::pow(this->value, exp)};
}

And the program produces the expected output:

c++ -std=c++11 pow.cpp -o pow
./pow
81^0.25 = 3

That solves the actual problem, but I wonder why it is necessary? Shouldn't the signature match allow the compiler to select the right function?

kamikaze
  • 1,529
  • 8
  • 18
  • 1
    It would be confusing with cases like global `foo(int)` vs `C::foo(char)`. – Jarod42 Dec 03 '15 at 13:22
  • [name lookup in C++](http://en.cppreference.com/w/cpp/language/lookup) is probably a bit more complicated than you expect. In this case, the lookup stopped as soon as it found `container::pow` (for which the argument types don't match, hence the error), and ADL was of no help either. – Sander De Dycker Dec 03 '15 at 13:34
  • Btw, putting `using std::pow` into the global namespace is *extremely* poor practice and error prone (as your example shows). – Walter Dec 03 '15 at 13:36
  • 1
    There shouldn't be a `pow()` function in the global `namespace` -- that's another example of poor design. Instead, the user-defined `pow()` function should be declared in the `namespace` in which the `value_type` is declared. – Walter Dec 03 '15 at 16:59

5 Answers5

6

You need to introduce the std::pow function in your pow function. This allows the compiler to fall back to std::pow if ADL fails

#include <iostream>
#include <cmath>

template <typename T>
struct container {
    T value;

    container<T> pow(T const exp) const {
        using std::pow;
        return {pow(this->value, exp)};
    }
};

int main() {
    container<double> value{81.};
    std::cout << value.value << "^0.25 = " << value.pow(.25).value << '\n';
    return 0;
}

Live Example

This is the same thing you would do when building a custom swap function. You can see it working with a class that has its own pow here


edit for those who don't understand lookup. It's important to understand the difference between

T func(T a, T b)
{
  using std::pow;
  return pow(a,b);
}

and

T func(T a, T b)
{
  return std::pow(a,b);
}

The latter always calls std::pow() and will fail if T cannot be converted to double (or std::complex<double> if <complex> was #included). The former will use ADL to find the best matching pow() function, which may be std::pow.

Community
  • 1
  • 1
NathanOliver
  • 171,901
  • 28
  • 288
  • 402
  • @Walter Yes and no. I mean, this doesn't answer to the `Shouldn't the signature match allow the compiler to select the right function?` part. Check my answer too, please. – Paolo M Dec 03 '15 at 13:40
  • @Walter : except that the desired `pow` is not necessarily in the `std` namespace, but rather in the global namespace : quote from OP : "The container<> offers a pow() method, that is supposed to rely on pow() being available from the underlying type in the global namespace." – Sander De Dycker Dec 03 '15 at 13:40
  • @SanderDeDycker you don't understand lookup: the recommended code does not necessarily call `std::pow`, but will call `::pow()` if required. – Walter Dec 03 '15 at 13:50
  • @Walter : give it a try to see if `::pow` is called if required - you might be surprised (I suggest trying to call a global `pow(int, int)` or a global `pow(const Bar::Foo&, const Bar::Foo&)`). In the example given by the OP (and in the two examples just mentioned), unqualified name lookup fails (because it stops at `container::pow`), and ADL isn't able to help. Adding `using std::pow` doesn't help in finding a `pow` in the global namespace, so it's not relevant to the OP's question. – Sander De Dycker Dec 03 '15 at 16:48
  • @SanderDeDycker Sometimes, it's better to not literally answer a question, in particular if it is of the form (such as this one) *how to get this bad code to work?* Instead, the answer should address the poor quality of the code. There is no reason to look into the global namespace: no `pow()` function should be in there (if it is, that's poor code). – Walter Dec 03 '15 at 17:02
  • @Walter : the question was not about how to get the code to work (the OP answered that for himself), but rather about *why* it didn't work : "I wonder why it is necessary? Shouldn't the signature match allow the compiler to select the right function?". Whether or not the `pow` functions should be in the global namespace is a different matter (and I agree with you on that), but pointing that out doesn't answer the OP's question. – Sander De Dycker Dec 03 '15 at 17:18
  • @SanderDeDycker The question (at least in the title) was not *why*, but *how*. – Walter Dec 03 '15 at 17:31
  • That answer makes a lot of sense, thank you! What I'm wondering, why does your example work? (That one: http://coliru.stacked-crooked.com/a/943f36ae247396ca) pow(Foo, Foo) is not in the std namespace, yet "using std::pow" allows using it. I would have expected that it should have been "using ::pow". – kamikaze Dec 04 '15 at 07:58
  • 1
    @kamikaze It is not that it allows it. putting `using std::pow;` in the function introduces `std::pow` to the overload set. The compile will use the arguments supplied to `pow` to determine which one to call. Since `Foo` has a `pow()` that matches it uses that. If `Foo` did not have a `pow` function it would try to use the `std` version and that would fail and give you a compiler error. Basically it boils down to this when having `using std::pow` in the function: Hey compiler call `pow` with these to arguments. If they don't have a `pow` try `std::pow`. – NathanOliver Dec 04 '15 at 12:51
2

This problem is not related with templates. Try out this code:

#include <iostream>
#include <cmath>

using std::pow;

struct container_double {
    double value;

     container_double pow(double const exp) const {
          return {pow(this->value, exp)};
     }
};

int main() {
     container_double value{81.};
     std::cout << value.value << "^0.25 = " << value.pow(.25).value << '\n';
     return 0;
}

This will produce the same error as yours. The problem is that (quoting from this answer):

the member function named foo is found at the class scope, and then name lookup will stop, so the global version foo will never be considered for overload resolution, even if the global version is more appropriate here. It is a kind of name hiding.

or from this one:

In general, when scopes are nested, any name declared in the inner scope hides any entities with the same name in an outer scope when that name is used in the inner scope. So, in this case, when used in the class scope, names declared in the class will hide those declared in the enclosing namespace.

Eventually, another similar behavior is that overriding a function in a derived class hides others overloads from the base class.

Community
  • 1
  • 1
Paolo M
  • 12,403
  • 6
  • 52
  • 73
0

Just use ::

template <typename T>
struct container {
    T value;

    container<T> pow(T const exp) const {
       return {::pow(this->value, exp)};
    }
};

Without :: only the pow of the struct is tested for matching. If not, if you made a mistake, you will use a global function without noticing it.

Jerome
  • 529
  • 4
  • 14
  • That answers title question, but not questions in body. – Jarod42 Dec 03 '15 at 13:23
  • @jarod42 the answer was so obvious given the title i didnt read all... My bad – Jerome Dec 03 '15 at 13:25
  • This is wrong. Inside `container::pow()`, we want to either calls `std::pow()` (for `double`, `float`, and `complex`) or a user-defined `pow(value_type,value_type)`. Your code does not achieve that. – Walter Dec 03 '15 at 13:38
  • This will not work if the datatype has its own `pow`. [[not working](http://coliru.stacked-crooked.com/a/e1f12aab44bd02e6)] [[working](http://coliru.stacked-crooked.com/a/943f36ae247396ca)] – NathanOliver Dec 03 '15 at 13:57
  • My bad, I wrote the title before I got to this answer myself. – kamikaze Dec 03 '15 at 15:56
0

The global pow is hidden by container::pow by the name hiding rule, which is in 3.3.10 paragraph 1 (*). So it's not visible when name lookup happens, thus, it's not found, so it can't participate in overload resolution. Since overload resolution is one of the most complicated parts of C++ already, having names from outer scopes interleaved with it might result in too many surprises; if functions from arbitrary outer scopes could get involved with the overload resolution for a particular function call, you might have to search far and wide to figure out why a particular overload resolution was occurring.

Common header files can bring a ton of stuff the average programmer is not aware of into global scope. (The rule is quite old and predates the decision to put all standard names in namespace std:: ... but we still need it anyway, because people use using directives (different from using declarations, which introduce only a single name) to bring a ton of names they don't necessarily know into a scope.)

For example, the header <algorithm> uses several names that are widely used by many programmers for totally different purposes; consider the function template std::count() , which has a name that many programmers might use for a loop index - or as a function that counts things. Or consider the function templates std::min() and std::max() . Many older codebases have their own min or their own max . (Though those are often macros, a ball of wriggly worms I won't get into.)

Mr Lister
  • 45,515
  • 15
  • 108
  • 150
llewelly
  • 339
  • 1
  • 4
0

The simple answer is: You don't force C++ to choose a function from the global namespace .(full stop)

Instead, you declare any functionality associated with user-defined types in the same namespace as the type, i.e.

namespace foo {
  struct bar      // the 'value_type'
  { 
    double x;
  };
  // define functions related to type bar in same namespace
  std::ostream& operator<<(std::ostream&s, bar x)
  {
    return s<<x.x;
  }

  foo::bar pow(foo::bar x, foo::bar y)
  {
    return {std::pow(x.x,y.x)};
  }
}

when

template <typename T>
struct container {
  T value;
  container<T> pow(T const&exp) const
  {
    using std::pow;
    return {pow(this->value, exp)};
  }
};

finds foo::pow() via ADL for T=foo::bar and std::pow() for T=double.

int main()
{
  container<foo::bar> value{81.};
  std::cout << value.value << "^0.25 = "
            << value.pow(foo::bar{0.25}).value << '\n';
}
Walter
  • 44,150
  • 20
  • 113
  • 196