【问题标题】:Conditionally enabled member functions in C++17C++17 中条件启用的成员函数
【发布时间】:2020-09-19 18:35:04
【问题描述】:

假设类型可以有FooBarBaz 方法,我们有类型特征来检查它。例如。对于Foo,我们有HasFoo

template <class Type>
constexpr bool  DetectFoo (decltype (std::declval<Type> ().Foo ())*)    { return true; }

template <class Type>
constexpr bool  DetectFoo (...)                                         { return false; }

template <class Type>
constexpr bool  HasFoo = DetectFoo<Type> (nullptr);

让我们编写一个Wrapper&lt;Type&gt; 类模板,它转发Type 的属性是否具有这些方法。对于任何Type,应满足以下条件:

  • Wrapper&lt;Type&gt; w; 应该可以编译,因为我们还没有调用方法。
  • w.X (); 应该编译 iff HasX&lt;Type&gt;, 对于 X = Foo, Bar, Baz
  • HasX&lt;Wrapper&lt;Type&gt;&gt; == HasX&lt;Type&gt; 应该成立,因为 X = Foo, Bar, Baz

要在Wrapper 中有条件地启用一个方法,在 C++20 中有一个明确的方法:

template <class Type>
struct Wrapper {
    void Foo ()
    requires HasFoo<Type>;
};

但是在没有概念的情况下,在早期的 C++ 标准中该怎么办?我想出了以下想法(见live demo):

template <class Type>
struct Wrapper {
    template <bool dummy = true, class = std::enable_if_t<HasFoo<Type> && dummy>>
    void Foo ();
};

This answer 说它格式错误,NDR,但我不明白解释。 NDR 真的格式不正确吗?

【问题讨论】:

  • class = std::enable_if_t&lt;HasFoo&lt;Type&gt; &amp;&amp; dummy&gt; 这应该做什么/意味着什么?我真的无法理解它,它显然不是格式正确的代码。
  • 也许你被否决的原因之一是你的问题有点令人困惑。有更多的解释,然后是显示你想要的代码。您对FooBarBazHasX 的描述很容易被误解。只需显示您想要的代码。代码比代码解释更容易理解。
  • 我还有更多问题。假设我们有一个带有方法FooBar 但没有Baz 的类型。然后我们已经可以在上面调用Foo()Bar(),但是Baz() 会因为编译时错误而失败。那么Wrapper 究竟应该做什么或在那里做什么?您可以从传入的类继承,您将自动获得该行为。这有助于使这个问题更不清楚您真正想要实现的目标。
  • @super:我已经编辑了这个问题。希望现在更好。 Wrapper&lt;Type&gt; 不能继承 Type 的形式,因为 Type 可能是 final 甚至不是一个类。例如,Wrapper&lt;Type&gt; 可以选择包含Type,而FooBarBaz 可以是比较运算符,它们应该存在于Wrapper&lt;Type&gt; 上,前提是它们存在于Type 上。

标签: c++ templates c++17 instantiation sfinae


【解决方案1】:

您的代码格式正确。

链接答案参考[temp.res.general]/8.1

可以在任何实例化之前检查模板的有效性。 ...程序格式错误,不需要诊断,如果:

——不能为模板生成有效的特化...并且模板没有被实例化,...

这里,我们所说的“模板”是Foo

我相信这可以用两种方式来解释:

(1) 我们可以认为Wrapper&lt;A&gt;::FooWrapper&lt;B&gt;::Foo 是同一个模板(对于每个A,B)。那么,Wrapper 的这种模板参数的存在使得enable_if_t 中的条件true 足以使代码格式正确。

(2) 我们也可以将Wrapper&lt;A&gt;::FooWrapper&lt;B&gt;::Foo 视为不同 模板(对于A != B)。然后,如果存在 Wrapper 的此类模板参数,而无法实例化 Foo,则您的代码将是格式错误的 NDR。但它永远不会发生,因为对于每个模板参数,您始终可以将 HasFoo 特化为 true

无论如何,我认为 (1) 是预期的解释。 [temp.res.general]/8.1 的目的不是妨碍您,而是尽可能早地验证模板来帮助您。我从未见过编译器使用第二种解释。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-02-19
    • 2021-11-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-06
    • 2017-04-04
    • 1970-01-01
    相关资源
    最近更新 更多