我想定义一个通用的强别名类型,即一个类型

template<typename T, auto ID = 0>
class StrongAlias {
    T value;
};

这样,对于T类型,可以使用与StrongAlias<T>完全相同的方式来使用T,但是StrongAlias<T, 0>StrongAlias<T, 1>是不同的类型,无法有效地相互转换。
为了尽可能完美地模仿T,我希望自己的StrongAlias具有与T相同的构造函数。
这意味着我想执行以下操作:
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)...) {}
};

除了自template parameter pack must be the last template parameter以来不起作用,如clang 5.0会告诉我的那样。
我想到的另一种使用SFINAE的方法是在返回类型中,但是由于构造函数没有返回类型,因此这似乎也不起作用。

有什么方法可以在构造函数中的可变参数模板参数包上使用SFINAE?

或者,如果没有,我可以用另一种方式完成我想要的吗?

请注意,就我而言,从T隐式构造是不够的,如StrongAlias<std::optional<int>>的示例所示:如果只能从StrongAlias隐式构造std::optional<int>,则不能从std::nullopt(std::nullopt_t类型)构造它。将涉及2个用户定义的转化。我真的很想拥有别名类型的所有构造函数。

编辑:
当然,如果不使用SFINAE从不兼容的参数构造StrongAlias,则可以实现该功能,并使程序无效。但是,尽管这在我的特定情况下是可以接受的行为,但是显然不是最佳的,因为StrongAlias可以用于查询给定类型是否可以从某些参数构造的模板中(通过std::is_constructible)。尽管这样会为std::false_type生成T,但会为std::true_type生成StrongAlias<T>,这可能意味着StrongAlias<T>不会出现不必要的编译错误,而对于T则不存在。

最佳答案

只需将std::enable_if_t更改为非类型模板参数即可:

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)...) {}
};

09-26 22:12