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
.
Just change std::enable_if_t
to a non-type template parameter:
template<typename T, auto ID = 0>
class StrongAlias {
T value;
public:
template<typename... Args, std::enable_if_t<std::is_constructible_v<T, Args...>, int> = 0>
StrongAlias(Args&&... args) noexcept(noexcept(T(std::declval<Args>()...)))
: value(std::forward<Args>(args)...) {}
};
The two issues with your snippet that make it not compile are
std::is_constructible<T, Args...>
as the first argument of std::enable_if_t
;decltype(...)
to a noexcept
operator.(There's also a third problem with the function style cast inside the noexcept
, but that only affects semantics, not compilability.)
Neither causes the error message you cite, which concerns a rule that doesn't apply to function templates at all. With these two problems fixed, Wandbox's Clang 5.0 happily accepts it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With