Apply a 'using std::foo' directive to a constructor initializer list locally (C++)
Asked Answered
P

1

13

Given a custom type, the following fragment shows the common approach for allowing a function to automatically select a user provided overload specific to the type, or a generic implementation of a function from the standard library if not.

// assume std::foo is a real function template returning an int
namespace a {
  struct b { };    
  int foo(b& ab) { ... }
}

int bar(a::b& ab)
{
  using std::foo;
  return foo(ab);
}

This approach will automatically pick a::foo in preference to std::foo if it exists.

My question is, is it possible to achieve a similar behaviour when the call in question is part of the initializer list of a constructor?

struct bar2
{
  bar2(a::b& ab);
  int i;
};

bar2::bar2(a::b& ab)
  : i{foo(ab)} // want a::foo if available, else std::foo
{ }

Obviously putting using std::foo in the constructor body is too late. However if I put it before the constructor definition I introduce std::foo to the global namespace which is also not desirable.

Is there any way to have the best of both worlds in this situation?

Perturbation answered 29/1, 2014 at 0:59 Comment(0)
E
5

According to Can a using statement appear in a constructor initialization list? one work around is to use a private static function like so:

struct bar2
{
  bar2(a::b& ab);
  int i;
  private:
  static int wrapper(a::b& f)
  {
      using std::foo;
      return foo(f);
  }
};

bar2::bar2(a::b& ab)
  : i{wrapper(ab)} // want a::foo if available, else std::foo
{ }

In this case, you can preserve the benefits of the initialization list without having to move the initialization to the body of the constructor. The OP in the question linked above claims that it doesn't provide ADL, but it seems to work for me. To test, simply remove:

int bar(foo f)
{
    std::cout << "custom.\n";
    return 0;
}
Ejective answered 29/1, 2014 at 2:7 Comment(2)
You can also do it with lambda to keep things close : bar2::bar2(a::b& ab) : i { [&] { using std::foo; return foo(ab); }() } {}Honeycomb
I'll accept this answer, although I'd probably prefer the lambda trick @Honeycomb suggested as it keeps the hack local. It's a bit of a shame that we have to resort to silly things like this to resolve name lookup issues.Perturbation

© 2022 - 2024 — McMap. All rights reserved.