SFINAE in variadic constructor

谁说胖子不能爱 提交于 2019-12-07 19:51:16

问题


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.


回答1:


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



回答2:


The two issues with your snippet that make it not compile are

  • Trying to pass a type std::is_constructible<T, Args...> as the first argument of std::enable_if_t;
  • Trying to pass 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.



来源:https://stackoverflow.com/questions/49372964/sfinae-in-variadic-constructor

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!