I want to define a generic strong alias type, i.e. a type
template<typename T, auto ID = 0>
class StrongAlias {
T value;
};
such that for a type T
a StrongAlias<T>
can be used in exactly the same way as T
, but StrongAlias<T, 0>
and StrongAlias<T, 1>
are different types that can not be implecitly converted to each other.
In order to mimic a T
as perfectly as possible, I would like my StrongAlias
to have the same constructors as T
.
This means I would like to do something like the following:
template<typename T, auto ID = 0>
class StrongAlias {
T value;
public:
// doesn't work
template<typename... Args, typename = std::enable_if_t<std::is_constructible_v<T, Args...>>>
StrongAlias(Args&&... args) noexcept(std::is_nothrow_constructible_v<T, Args...>)
: value(std::forward<Args>(args)...) {}
};
except that this wouldn't work since the template parameter pack must be the last template parameter
, as clang 5.0 would tell me.
The other way to use SFINAE that I thought of would be in the return type, but since a constructor doesn't have a return type, this does not seem to work either.
Is there any way to use SFINAE on a variadic template parameter pack in a constructor?
Alternatively, if there isn't one, can I accomplish what I want in another way?
Note that being implicitly constructible from a T
isn't enough in my case, as the example of StrongAlias<std::optional<int>>
shows: If StrongAlias
can only be implictly constructed from a std::optional<int>
, it cannot be be constructed from a std::nullopt
(of type std::nullopt_t
), because that would involve 2 user-defined conversions. I really want to have all constructors of the aliased type.
EDIT:
Of course it would be possible to implement this without SFINAE and let the program be invalid if a StrongAlias
is constructed from incompatible arguments. However, while this would be an acceptable behaviour in my specific case, it is clearly not optimal as the StrongAlias
may be used in a template that queries if the given type is constructible from some arguments (via std::is_constructible
). While this would yield a std::false_type
for T
, it would result in a std::true_type
for StrongAlias<T>
, which could mean unnecessary compile errors for StrongAlias<T>
that wouldn't exist for T
.