【问题标题】:Disabling a template class member function with SFINAE使用 SFINAE 禁用模板类成员函数
【发布时间】:2019-02-10 20:31:48
【问题描述】:

假设我有一个接受某种类型T 的类。这意味着它可以接受某些类型optional<U>。如果不是optional 类型的功能,我想禁用它,但如果是...那么我想知道U 类型。

我已经能够通过模板禁用该功能,但我不知道如何处理检测模板模板类而不编写相同的类两次并制作一个模板模板版本。

Code:

class Dummy{};

template <typename T>
class C {
    T t;
    
public:
    C(T t) : t(std::move(t)) { }
    
    T get() {
        return t;
    }
    
    // Will clearly fail when T doesn't have a value_type
    template <typename R = T, typename OptT = typename T::value_type, typename = std::enable_if_t<std::is_same_v<T, optional<OptT>>>>
    std::vector<OptT> stuff() {
        std::vector<OptT> vec;
        // Do stuff, fill vec
        return vec;
    }
};

int main() {
    C<Dummy> c{Dummy()};                // Error
    // C<optional<Dummy>> c{Dummy()};   // Works fine
    c.get();
}

这样做,我得到了

main.cpp:在 'class C' 的实例化中:

main.cpp:33:14:从这里需要

main.cpp:25:23: 错误:“class Dummy”中没有名为“value_type”的类型

 std::vector<OptT> stuff() {

                   ^~~~~

注意:如果有这个类的特化也没关系,我只关心检测它是否std::optional。我不需要它为任何其他类型工作......只是可选的。这可能允许某种模板专业化,但我在研究它时没有弄清楚如何做到这一点。

如何让这个函数只在类型为std::optional的时候出现,然后当是那个类型的时候,能不能抓取到可选里面的类型呢?我可以在不触及T 的模板定义的情况下执行此操作吗? (例如,我可以在将其保留为 template &lt;typename T&gt; 而不将其更改为 template &lt;template &lt;typename&gt; T&gt; 或必须复制上述两者的类的情况下这样做)

【问题讨论】:

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


    【解决方案1】:

    你的问题在这里:

    // Will clearly fail when T doesn't have a value_type
    template <typename R = T,
              typename OptT = typename T::value_type,
              typename = std::enable_if_t<std::is_same_v<T, optional<OptT>>>>
    

    您是否引入了一个新的虚拟模板参数R,但您仍在使用旧的T 进行所有检查。所以没有一个检查实际上是依赖的。把它们换成R就可以了。


    另一种方法是仅使用标签参数来推迟到不同的函数:

    template <typename> struct tag { };
    
    template <typename R=T>
    auto stuff() -> decltype(stuff_impl(tag<R>{})) {
        return stuff_impl(tag<R>{});
    }
    

    现在你可以有效地使用普通的模板推导来提取类型:

    template <typename U>
    std::vector<U> stuff_impl(tag<std::optional<U>>) {
        return {};
    }
    

    【讨论】:

      【解决方案2】:

      我建议定义一个自定义类型特征如下

      template <typename>
      struct optionalType
       { };
      
      template <typename T>
      struct optionalType<std::optional<T>>
       { using type = T; };
      

      当且仅在使用std::optional 调用时定义typestd::optional&lt;T&gt; 的类型T)。

      现在你的stuff() 变成了

      template <typename R = T,
                typename OptT = typename optionalType<R>::type>
      std::vector<OptT> stuff() {
          std::vector<OptT> vec;
          // Do stuff, fill vec
          return vec;
      }
      

      观察OptTstd::optional 的类型,仅在R(又名T)是std::optional 时出现;你有一个替换失败,否则,禁用stuff()

      你可以验证

      C<Dummy>                c0{Dummy()};
      C<std::optional<Dummy>> c1{Dummy()};
      
      //c0.stuff(); // compilation error
      c1.stuff();   // compile
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-10-03
        相关资源
        最近更新 更多