简体   繁体   English

使用SFINAE禁用模板类成员函数

[英]Disabling a template class member function with SFINAE

Suppose I have a class that accepts some type T . 假设我有一个接受某种类型T This means it could accept some type optional<U> . 这意味着它可以接受某些类型的optional<U> I want to disable a function if it is not of an optional type, but if it is... then I want to know that type U . 如果它不是optional类型,我想禁用该功能,但是如果它是...,那么我想知道该类型U

I've been able to to disable the function via templates, but I don't know how to handle detecting a templated template class without writing the same class twice and making one a templated template version. 我已经能够通过模板禁用该功能,但是我不知道如何在不两次编写同一个类并将一个模板模板版本化的情况下检测模板化模板类。

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();
}

In doing this, I get 通过这样做,我得到

main.cpp: In instantiation of 'class C': main.cpp:在“ C类”的实例中:

main.cpp:33:14: required from here main.cpp:33:14:从这里开始

main.cpp:25:23: error: no type named 'value_type' in 'class Dummy' main.cpp:25:23:错误:``类虚拟''中没有名为``value_type''的类型

  std::vector<OptT> stuff() { ^~~~~ 

Note : It is okay if there is a specialization of this class, all I care about is detecting if its std::optional . 注意 :如果有此类的专业化,这是可以的,我所关心的就是检测其std::optional I do not need it to work for any other type... only optional. 我不需要它为任何其他类型工作……只是可选的。 This may allow for some kind of template specialization but I didn't figure out how to do that when researching it. 这可能允许某种形式的模板专业化,但是在研究它时我没有弄清楚该如何做。

How can I make this function only appear when the type is std::optional , and then when it is that type, be able to grab the type inside the optional? 我怎样才能使此函数仅在类型为std::optional ,然后当它是该类型时,才能够在Optional内部获取类型? Can I do this without touching the template definition of T ? 我可以不触碰T的模板定义吗? (as in, can I do it while leaving it as template <typename T> without changing it to template <template <typename> T> or having to copy this class where both the above are made) (例如,我可以将其保留为template <typename T>而不将其更改为template <template <typename> T>或不必复制以上两者的类来做吗?)

I suggest to define a custom type traits as follows 我建议如下定义一个自定义类型特征

template <typename>
struct optionalType
 { };

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

that define type (the type T of the std::optional<T> ) when and only when invoked with a std::optional . 当且仅当使用std::optional调用时定义typestd::optional<T>的类型T )。

Now your stuff() simply become 现在,您的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;
}

Observe that OptT , the type of the std::optional , is present only when R (aka T ) is a std::optional ; OptT ,只有当R (aka T )是std::optional时,才出现OptT ,即std::optional的类型。 you have a substitution failure, otherwise, that disable stuff() . 您将发生替换失败,否则将禁用stuff()

You can verify that 您可以验证

C<Dummy>                c0{Dummy()};
C<std::optional<Dummy>> c1{Dummy()};

//c0.stuff(); // compilation error
c1.stuff();   // compile

Your problem here: 您的问题在这里:

// 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>>>>

Is that you introduced a new dummy template parameter, R , but you're still using the old one, T , for all your checks. 是您引入了一个新的虚拟模板参数R ,但是您仍然使用旧的T来进行所有检查。 So none of the checks are actually dependent. 因此,所有检查实际上都不依赖。 Swap them to R and you're fine. 将它们交换到R ,就可以了。


A different approach would be to defer to a different function using just a tag parameter: 一种不同的方法是仅使用标记参数来遵循不同的功能:

template <typename> struct tag { };

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

Where now you can effectively just use normal template deduction to pull out the type: 现在,您可以有效地使用常规模板推导来提取类型:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM