3

If I have a widely-used class template called Foo that I want to rename to Bar without having to update all of its users atomically, then up until C++17 I could simply use a type alias:

template <typename T>
class Bar {
 public:
  // Create a Bar from a T value.
  explicit Bar(T value);
};

// An older name for this class, for compatibility with callers that haven't
// yet been updated.
template <typename T>
using Foo = Bar<T>;

This is very useful when working in a large, distributed codebase. However as of C++17 this seems to be broken by class template argument deduction guides. For example, if this line exists:

template <typename T>
explicit Foo(T) -> Foo<T>;

then the obvious thing to do when renaming the class is to change the Foos in the deduction guide to Bars:

template <typename T>
explicit Bar(T) -> Bar<T>;

But now the expression Foo(17) in a random caller, which used to be legal, is an error:

test.cc:42:21: error: alias template 'Foo' requires template arguments; argument deduction only allowed for class templates
  static_cast<void>(Foo(17));
                    ^
test.cc:34:1: note: template is declared here
using Foo = Bar<T>;
^

Is there any easy and general way to give a class with deduction guides two simultaneous names in a fully compatible way? The best I can think of is defining the class's public API twice under two names, with conversion operators, but this is far from easy and general.

jacobsa
  • 5,719
  • 1
  • 28
  • 60
  • You get some of the conversions for free if you derive the old class from the new class. But there's no perfect solution if your API users have mixed `auto&` and `Foo&`, which is quite possible in an older code base. – MSalters Sep 27 '21 at 07:13

2 Answers2

5

Your problem is exactly what P1814R0: Wording for Class Template Argument Deduction for Alias Templates wants to solve, that is to say, in C++20, you only need to add deduction guides for Bar to make the following program well-formed:

template <typename T>
class Bar {
 public:
  // Create a Bar from a T value.
  explicit Bar(T value);
};

// An older name for this class, for compatibility with callers that haven't
// yet been updated.
template <typename T>
using Foo = Bar<T>;

template <typename T>
explicit Bar(T) -> Bar<T>;

int main() {
  Bar bar(42);
  Foo foo(42); // well-formed
}

Demo.

But since it is a C++20 feature, there is currently no solution in C++17.

康桓瑋
  • 33,481
  • 5
  • 40
  • 90
0

Have you tried to define a macro?

#define Foo Bar;

(Personally I'd find it confusing with multiple names for same implementation, but I'm not you.)

Sorry I can't test at the moment, but I hope it works!

Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
  • Seriously, no. That breaks all kind of things. Macro's don't obey name spaces. – MSalters Sep 27 '21 at 07:14
  • I'm really surprised I didn't consider this actually. The name happens to be unique enough that this might not be a _terrible_ idea as a very temporary hack. Thanks. – jacobsa Sep 27 '21 at 07:15