Примените директиву 'using std:: foo' в список инициализатора конструктора локально (С++)

В соответствии с пользовательским типом, следующий фрагмент показывает общий подход, позволяющий функции автоматически выбирать предоставленную пользователем перегрузку, специфичную для этого типа, или общую реализацию функции из стандартной библиотеки, если нет.

// 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);
}

Этот подход автоматически выбирает a::foo вместо std::foo, если он существует.

Мой вопрос: возможно ли добиться аналогичного поведения, когда рассматриваемый вызов является частью списка инициализаторов конструктора?

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

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

Очевидно, что положить тело using std::foo в тело конструктора слишком поздно. Однако, если я поставил его перед определением конструктора, я введу std::foo в глобальное пространство имен, которое также нежелательно.

Есть ли способ получить лучшее из обоих миров в этой ситуации?

Ответ 1

В соответствии с Может ли оператор using появиться в списке инициализации конструктора? одна работа заключается в использовании частной статической функции, например:

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
{ }

В этом случае вы можете сохранить преимущества списка инициализации без необходимости переместить инициализацию в тело конструктора. OP в связанном выше вопросе утверждает, что он не предоставляет ADL, но работает для меня. Чтобы проверить, просто удалите:

int bar(foo f)
{
    std::cout << "custom.\n";
    return 0;
}