【问题标题】:SFINAE in variadic constructor可变参数构造函数中的 SFINAE
【发布时间】:2018-08-28 14:28:44
【问题描述】:

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

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

这样对于类型TStrongAlias&lt;T&gt; 可以以与T 完全相同的方式使用,但StrongAlias&lt;T, 0&gt;StrongAlias&lt;T, 1&gt; 是不同的类型,不能相互隐式转换。 为了尽可能完美地模仿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&lt;std::optional&lt;int&gt;&gt; 的示例所示:如果 StrongAlias 只能从 std::optional&lt;int&gt; 隐式构造,则无法构造来自std::nulloptstd::nullopt_t 类型),因为这将涉及 2 个用户定义的转换。我真的很想拥有别名类型的所有构造函数。

编辑: 当然,可以在没有 SFINAE 的情况下实现这一点,如果 StrongAlias 是由不兼容的参数构造的,则程序无效。然而,虽然这在我的特定情况下是可接受的行为,但它显然不是最佳的,因为StrongAlias 可以用于查询给定类型是否可以从某些参数构造的模板中(通过std::is_constructible)。虽然这会为T 生成一个std::false_type,但它会为StrongAlias&lt;T&gt; 生成一个std::true_type,这可能意味着StrongAlias&lt;T&gt; 的不必要的编译错误对于T 不存在。

【问题讨论】:

  • 哪个 clang 5.0 告诉你这个?

标签: c++ templates constructor variadic-templates c++17


【解决方案1】:

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

【讨论】:

  • 我在想,你是否应该在std::is_constructible_v&lt;T, Args...&gt;的packexpansion中写Args...Args&amp;&amp;...
  • @Kilian 使用Args... 就足够了。它们是一样的,因为std::declval 会自动添加&amp;&amp;en.cppreference.com/w/cpp/types/is_constructible
  • 感谢您的回复!另一个问题:显式呢?虽然 T 的某些构造函数可能是显式的,也可能不是显式的,但 StrongAlias 的构造函数现在始终是非显式的。这同样适用于 constexpr。但我认为你可以通过构造构造函数 constexpr 来解决这个问题。
【解决方案2】:

导致无法编译的 sn-p 的两个问题是

  • 尝试将类型std::is_constructible&lt;T, Args...&gt; 作为std::enable_if_t 的第一个参数传递;
  • 试图将decltype(...) 传递给noexcept 运算符。

noexcept 中的函数样式转换还有第三个问题,但这只会影响语义,不会影响可编译性。)

两者都不会导致您引用的错误消息,该错误消息涉及根本不适用于函数模板的规则。解决了这两个问题后,Wandbox 的 Clang 5.0 很高兴地接受了它。

【讨论】:

  • 感谢您指出我在此处从记忆中输入的错误,并修复了它(我使用is_constructible_v 而不是is_constructible 并且在我的原始代码中没有 noexcept 说明符)
  • @Irgendwhy 请提供一个minimal reproducible example,它实际上会产生您报告的错误消息。固定示例在 Wandbox 上编译得很好。
  • 你是对的,但我发誓我在原始代码中收到了该错误消息!没关系,它现在肯定有效,问题的整个前提似乎是无效的。我应该删除它吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-06-04
  • 1970-01-01
  • 2017-01-14
  • 1970-01-01
  • 1970-01-01
  • 2011-09-05
  • 2016-09-02
相关资源
最近更新 更多