如何“加强”模板化函数的参数?

问题描述 投票:0回答:2

我有两种相关的类型(鸭子类型),另一种提供类似的功能但具有不同的界面:

namespace a
{
    template<typename T>
    struct A final {};

    using int_t = A<int>;
    using float_t = A<float>;
}
namespace b
{
    template<typename T>
    struct B final {};

    using int_t = B<int>;
    using float_t = B<float>;
}

namespace foo
{
    template<typename T>
    struct Foo final {};

    using int_t = Foo<int>;
    using float_t = Foo<float>;

    std::string f(const int_t&)
    {
        return "f(Foo<int>)\n";
    }
    std::string g(const int_t&)
    {
        return "g(Foo<int>)\n";
    }
    std::string h(const float_t&)
    {
        return "h(Foo<float>)\n";
    }
}

a
b
是“相关”,
foo
是“相似”。写一个通用的
f()
可以,但是太“贪婪”了:

template<typename T>
std::string f(const T&)
{
    return "f(T)\n";
}

// ...

int main()
{
    a::int_t a_int;
    b::int_t b_int;
    foo::int_t foo_int;

    std::cout << f(a_int); // "f(T)"
    std::cout << f(b_int); // "f(T)"
    std::cout << f(foo_int); // "f(Foo<int>)"
    std::cout << f(314); // NO! ... "f(T)"
    std::cout << "\n";
}

为了“收紧”一点,我做了以下操作:

template<typename Tab> struct AB final
{
    AB() = delete;
};
template<typename T> struct AB<a::A<T>> final
{
    AB() = delete;
    using type = a::A<T>;
};
template<typename T> struct AB<b::B<T>> final
{
    AB() = delete;
    using type = b::B<T>;
};

现在我可以编写仅适用于

g()
a::A<T>
b:B<T>
:

template<typename Tab, typename type_ = typename AB<Tab>::type>
std::string g(const Tab&)
{
    return "g(Tab)\n";
}

这按预期工作:

    std::cout << g(a_int); // "g(Tab)"
    std::cout << g(b_int); // "g(Tab)"
    std::cout << g(foo_int); // "g(Foo<int>)"
    //std::cout << g(314); // YES! doesn't compile 
    std::cout << "\n";

但是,我不知道如何设置来编写仅适用于

h()
a::A<float>
b::B<float>
,但不适用于任何其他专业化。

template<typename Tab, typename type_ = typename AB<Tab>::type>
std::string h(const Tab&)
{
    return "h(Tab<float>)\n";
}

// ...

    a::float_t a_float;
    b::float_t b_float;
    foo::float_t foo_float;

    std::cout << h(a_float); // "h(Tab<float>)";
    std::cout << h(b_float); // "h(Tab<float>)";
    std::cout << h(foo_float); // "h(Foo<float>)"
    std::cout << h(a_int); // NO! ... "h(Tab<float>)";
    std::cout << h(b_int); // NO! ... "h(Tab<float>)";
    //std::cout << h(foo_int); // YES! doesn't compile 
    //std::cout << h(314); // YES! doesn't compile 

我尝试过将模板作为

template
传递,但我不太清楚正确的语法。

我想要一些可以在 C++14 中运行的东西。

c++ templates c++14 metaprogramming template-templates
2个回答
1
投票

我不知道如何设置来编写一个仅适用于

a::A<float>
b::B<float>
的 h(),但不适用于任何其他专业化。

您可以添加约束。

C++20:

template <template <class> class Tab, class T>
    requires(std::same_as<a::float_t, Tab<T>> ||
             std::same_as<b::float_t, Tab<T>>)
std::string h(const Tab<T>&) {
    return "h(Tab<float>)\n";
}

C++14:

template <template <class> class Tab, class T>
std::enable_if_t<std::is_same<a::float_t, Tab<T>>::value ||
                 std::is_same<b::float_t, Tab<T>>::value,
                 std::string>
h(const Tab<T>&) {
    return "h(Tab<float>)\n";
}

在这两个版本中,您都使函数采用模板-模板参数 (

Tab
),然后检查
Tab<T>
是否为
a::float_t
b::float_t
SFINAE 样式以允许其他
h
重载。


如果您不需要 SFINAE 创建其他

h
重载,则
static_assert
可能更可取:

template <template <class> class Tab, class T>
std::string h(const Tab<T>&) {
    static_assert(std::is_same<a::float_t, Tab<T>>::value ||
                  std::is_same<b::float_t, Tab<T>>::value,
                  "must be a::float_t or b::float_t");
    return "h(Tab<float>)\n";
}

1
投票

使实施与您已有的保持一致,您可以这样做:

  template<template<typename> typename Tab> struct AB final
  {
      AB() = delete;
  };
  
  template<> struct AB<a::A> final
  {
      AB() = delete;
      using type = a::A<float>;
  };
  
  template<> struct AB<b::B> final
  {
      AB() = delete;
      using type = b::B<float>;
  };  
        
  template<template <typename> typename Tab, typename = typename AB<Tab>::type>
  std::string h(const Tab<float>&)
  {
      return "h(Tab<float>)\n";                        
  }  

那么不应该编译的东西就不会编译。

注意,里面的

using type = a::A<float>;
仅用于识别。还可以写

  template<template <typename> typename Tab, typename = typename AB<Tab>::type>
  std::string h(const Tab<int>&)
  {
      return "h(Tab<int>)\n";                        
  }  

不改变

AB

我个人会在这个设计上使用

static_assert
enable_if
。尽管如此,这是一个值得拥有的选择。

© www.soinside.com 2019 - 2024. All rights reserved.