C++模板专业化 - 避免重新定义

haa*_*ael 4 c++ templates sfinae type-traits template-specialization

我想要一个接受不同类型参数的泛型函数(或方法).如果提供的类型具有"one"方法,则该函数应使用它.如果它有'两个'方法,该函数应该使用它.

这是无效的代码:

#include <iostream>

template<typename Type> void func(Type t)
{
    t.one();
}

template<typename Type> void func(Type t) // redefinition!
{
    t.two();
}

class One
{
    void one(void) const
    {
        std::cout << "one" << std::endl;
    }
};

class Two
{
    void two(void) const
    {
        std::cout << "two" << std::endl;
    }
};

int main(int argc, char* argv[])
{
    func(One()); // should print "one"
    func(Two()); // should print "two"
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

是否可以使用SFINAE?是否可以使用type_traits?


澄清:

如果可以使用SFINAE,我会更高兴.最好的情况是:使用第一个模板,如果失败则使用第二个模板.

检查方法存在只是一个例子.我真正想要的还是检查与其他类的兼容性.

任务可以改写:

  1. 如果该类支持第一个接口,请使用它.
  2. 如果第一个接口出现故障,请使用第二个接口.
  3. 如果两者都失败,则报告错误.

Sto*_*ica 5

是的,这是可能的.在C++ 11中,它甚至相对容易.

#include <iostream>
#include <type_traits>

template<class, typename = void>
struct func_dispatch_tag :
  std::integral_constant<int, 0> {};

template<class C>
struct func_dispatch_tag<C, 
  std::enable_if_t<std::is_same<decltype(&C::one), void (C::*)() const>::value>
  > : std::integral_constant<int, 1> {};

template<class C>
struct func_dispatch_tag<C,
  std::enable_if_t<std::is_same<decltype(&C::two), void (C::*)() const>::value>
  > : std::integral_constant<int, 2> {};

template<class C>
void func(C const&, std::integral_constant<int, 0>) {
    std::cout << "fallback!\n";
}

template<class C>
void func(C const &c, std::integral_constant<int, 1>) {
    c.one();
}

template<class C>
void func(C const &c, std::integral_constant<int, 2>) {
    c.two();
}

template<class C>
void func(C const &c) {
    func(c, func_dispatch_tag<C>{});
}

struct One
{
    void one(void) const
    {
        std::cout << "one\n";
    }
};

struct Two
{
    void two(void) const
    {
        std::cout << "two\n";
    }
};

struct Three {};

int main(int argc, char* argv[])
{
    func(One()); // should print "one"
    func(Two()); // should print "two"
    func(Three());
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

重点:

  1. 我们SFINAE上的第二个参数func_dispatch_tag.编译器查看导致参数的所有模板特化<C, void>.由于任何后者是"更专门的"时,不会发生SF(即,当std::enable_if_tvoid),它被选择的.

  2. 所选择的特征特化定义了一个标签,我们对其进行标签调度.标签分派取决于函数重载,而不是函数模板特化(不能部分专门化).

  3. 您可以定义回退功能(就像我做的那样),或者static_assert.我们可以定义的标签数量仅受int的范围限制,因此扩展到其他成员只需添加另一个特化func_dispatch_tag.

  4. 该成员必须是可访问的,否则将发生SF.此外,具有两个成员的类将导致歧义.记住这一点.

  • 如果不是这样写会不好玩,不是吗?哈哈 (3认同)
  • 加上1使用积分常数基类的优雅.减1实际上没有检查方法(如果一个和两个都存在歧义) (2认同)