使用不依赖于方法模板参数的enable_if

Luk*_*rth 14 c++ templates sfinae variadic-templates c++11

我正在尝试使用std::enable_if和SFINAE完全基于类的模板参数来切换类模板的方法的实现.例:

#include <type_traits>

template<class T1, class T2>
class Foo {
    template<class InnerT, class ... Args>
    typename std::enable_if<std::is_same<T1, T2>::value, void>::type
    bar(InnerT param) {};

    template<class InnerT, class ... Args>
    typename std::enable_if<!std::is_same<T1, T2>::value, void>::type
    bar(InnerT param) {};
};


int main() {
    Foo<int, int> f;
}
Run Code Online (Sandbox Code Playgroud)

在这里,bar()应该表现不同根据是否T1T2属于同一类型或不.但是,此代码无法编译.GCC和clang都没有告诉我任何有用的东西.我怀疑问题是std::enable_if条件不依赖于参数bar(),即不依赖于标准第17.8.2点第8点规定的直接上下文.

这个代码很好地支持了这个假设:

#include <type_traits>

class DummyClass {};

template<class T1, class T2>
class Foo {
    template<class InnerT, class ... Args>
    typename std::enable_if<std::is_same<T1, T2>::value || 
                            std::is_same<InnerT, DummyClass>::value, void>::type
    bar(InnerT param) {};

    template<class InnerT, class ... Args>
    typename std::enable_if<!std::is_same<T1, T2>::value || 
                            std::is_same<InnerT, DummyClass>::value, void>::type
    bar(InnerT param) {};
};


int main() {
    Foo<int, int> f;
}
Run Code Online (Sandbox Code Playgroud)

现在里面的表达式std::enable_if取决于"立即上下文",即InnerT,即使表达式的那一部分总是求值为false.

看起来我可以使用它作为一种解决方法,但这感觉真的很丑陋和丑陋.你如何"正确"解决这个问题?我的想法是添加一个额外的模板参数(调用它DummyType)bar(),默认为例如DummyType = T1,然后检查std::is_same<DummyType, T2>,但bar()采用参数包的事实使这不可能(或者它...?)

Cal*_*eth 15

而不是尝试SFINAE进入两个实现,只需使用正常的重载分辨率.

#include <type_traits>
#include <iostream>

template<class T1, class T2>
class Foo {
    template<class InnerT, class ... Args>
    void do_bar(InnerT param, std::true_type, Args... args) { std::cout << "same" << std::endl; }

    template<class InnerT, class ... Args>
    void do_bar(InnerT param, std::false_type, Args... args) { std::cout << "not same" << std::endl; }

public:
    template<class InnerT, class ... Args>
    void bar(InnerT&& param, Args&&... args) 
    {
        do_bar(std::forward<InnerT>(param), std::is_same<T1, T2>{}, std::forward<Args>(args)...);
    }

};

int main() {
    Foo<int, int> f1;
    Foo<int, double> f2;

    f1.bar(1, 2, 3);
    f2.bar("Hello");
}
Run Code Online (Sandbox Code Playgroud)

现场观看

  • "BinaryTypeTrait描述了两种类型之间的关系......它应该......直接或间接地从它的基本特征**公开和明确地导出,这是模板integral_constant的一种特殊化"[`[meta .rqmts]`](http://eel.is/c++draft/meta.rqmts#def:BinaryTypeTrait) (7认同)
  • 谢谢!我已经使用过那些,并且总是打算检查. (2认同)

小智 7

要从评论中扩展:

我的想法是添加一个额外的模板参数(调用它DummyType)bar(),默认为例如DummyType = T1,然后检查std::is_same<DummyType, T2>,但bar()采用参数包的事实使这不可能(或者它...?)

它没有.完全按照你的想法行事是行不通的.

#include <type_traits>

template<class T1, class T2>
struct Foo {
    template<class InnerT, class ... Args, class DummyType = T1>
    typename std::enable_if<std::is_same<DummyType, T2>::value, void>::type
    bar(InnerT param) {};

    template<class InnerT, class ... Args, class DummyType = T1>
    typename std::enable_if<!std::is_same<DummyType, T2>::value, void>::type
    bar(InnerT param) {};
};


int main() {
    Foo<int, int> f;
    f.bar(3);                   // InnerT = int; Args = empty; DummyType = int.
    f.bar<int, void, short>(4); // InnerT = int; Args = void, short; DummyType = int.
}
Run Code Online (Sandbox Code Playgroud)

但是,如果我添加DummyType作为第二个模板参数,再后来通过的模板参数列表,应该进入的包 - 那第二个参数不应该去现在怎么做编译成DummyType,但要这部分的第一件事ARGS?

这就是我添加为最后一个参数的原因.如果模板包参数具有默认值,则允许它们遵循模板包参数.编译器将使用所有显式指定的参数Args,并且DummyType = T1无论您指定哪个参数都将使用.

  • 今天我学会了......!我不知道模板包后可能会出现默认参数.谢谢! (2认同)