【问题标题】:What's an alternative to traits for selecting between two different named functions?在两个不同的命名函数之间进行选择的特征的替代方法是什么?
【发布时间】:2016-04-07 20:22:18
【问题描述】:

还有......我不想使用函数指针,我真的想直接使用函数本身(因此可以内联或应用其他优化)。

假设:我有一个模板函数/类,它将计算一些数学内容,模板参数是整数类型,可能是 unsigned int32_t 或 unsigned int64_t。

在某些时候我需要随机数,所以我需要一个生成器,在一种情况下我将使用mt19937,在另一种情况下使用mt19937_64。所以实际的类型名称是不同的,但我必须选择一个并实际写在源代码中。

显然,整数类型的特征类可以正常工作(这就是我现在正在做的事情)。但在我看来,这种一次性使用在语法方面相当重量级,而且有点非本地(如果你明白我的意思,请阅读源代码)。

另一种方法是将生成器的使用封装在一些(通用)函数中,并为我的两个整数类型提供它的完全专业化。这其实没问题。

但是:还有其他选择吗?我可以在这里使用某种编译时“if”或“switch”(不完全是enable-if,它启用/禁用模板实例化)吗?或者别的什么(也许比我没看到的模板元编程更简单)?

(P.S. 请不要挂断mt19937 和mt19937_64 - 我知道它们都是我可以用我的整数类型实例化自己的类型的别名 - 但我更愿意使用标准定义他们的大量相当神奇的数字。另外,我不仅对mt19937/mt19937_64 感兴趣,还对其他类似案例感兴趣。)

这是我的特征类的代码目前的样子:

template <class Base>
struct traits { };

template <>
struct traits<unsigned __int32>
{
    using base_t = unsigned __int32;
    static const int nbits = std::numeric_limits<base_t>::digits;
    using random_engine_t = std::mt19937;
    ...
};

template <>
struct traits<unsigned __int64>
{
    using base_t = unsigned __int64;
    static const int nbits = std::numeric_limits<base_t>::digits;
    using random_engine_t = std::mt19937_64;
    ...
};

【问题讨论】:

  • 请发布您用于特征的代码。
  • 特征有什么问题?是不是你只需要 traits&lt;T&gt;::random_engine_t 在一个地方,所以你不喜欢样板?
  • 差不多,而且样板远离使用点。这意味着您必须为抽象发明一个名称并记住它,而不是仅在您想要使用它们的地方直接/就地使用两个原始名称。 (当然,围绕着一些额外的语法,希望对于“合理”的某些 C++ 相对值来说“相当容易阅读”。)
  • 您可能会无缘无故地担心函数指针。如果foo(void(*)) 是inline,调用foo(&amp;bar) 将允许优化器内联foo,发现对于那个特定内联实例调用的函数总是bar,然后继续内联。
  • @MSalters - 很好的提示 - 我会在生成的程序集中检查一下,谢谢。

标签: c++ templates c++11 template-meta-programming


【解决方案1】:

方法 #1:生成干净的分布式特征类型。

一些实用程序:

template<class T>struct tag_t{using type=T;};
template<class T>constexpr tag_t<T> tag = {};
template<class Tag>using type=typename Tag::type;

现在,我们创建标记重载而不是特征类:

tag<std::mt1337> which_random_engine( tag_t<int> );
tag<std::mt1337_64> which_random_engine( tag_t<std::int64_t> );

它可以让你在任何地方进行这样的重载。

我们可以用它来定义一个traits类:

template<class T>
using random_engine_for = type<decltype(which_random_engine(tag<T>))>;

用途:

random_engine_for<T> engine;

方法 #2:

template<class A, class B> struct zpair_t {};
template<class T, class...> struct lookup_t {};
template<class T, class A, class B, class...Ts>
struct lookup_t<T, zpair_t<A, B>, Ts...>:lookup_t<T, Ts...>{};
template<class T, class B, class...Ts>
struct lookup_t<T, zpair_t<T, B>, Ts...>:tag_t<B>{};
template<class T, class Default, class...Ts>
struct lookup_t<T, tag_t<Default>, Ts...>:tag_t<Default>{
  static_assert(sizeof(Ts...)==0, "Default must be last");
};

template<class A, class B> using kv=zpair_t<A,B>;
template<class Default> using otherwise=tag_t<Default>;
template<class T, class...KVs>
using lookup = type<lookup_t<T, KVs...>>;

using random_engine_t = 
  lookup< T,
    kv< int, std::mt19937 >,
    kv< std::int64_t, std::mt19937_64 >,
    otherwise<void> // optional
  >;

它使用一些相同的实用程序,并执行编译时类型映射。

我确信boost 在语法上有更好的变化。

【讨论】:

  • 语法基本相同...mpl::pair 代替zpair,mpl::at&lt;mpl::map&lt;pairs...&gt;, T&gt; 代替lookup&lt;T, pairs...&gt;。
  • 标签方法非常酷,我对标签调度非常满意。不知道它可以这么容易地打包。我现在和std::conditional比较对比。
  • 哦,顺便说一句,谢谢你提醒我别名模板,在这里也非常有用。
  • 注意方法#1需要tag_t&lt;std::mt19937&gt;作为which_random_engine的返回值才能编译。
【解决方案2】:

你可以使用std::conditional:

template <class Int>
using engine_t = std::conditional_t<
    std::is_same<Int, uint32_t>{},
    std::mt19937,
    std::mt19937_64
>;

假设Int 只能是uint32_t 或uint64_t。随着您最终使用的类型越多,这会变得越来越复杂。它也有安全问题 - 如果现在支持uint16_t 怎么办?您最终会默默地使用mt19937_64,这可能不是正确的决定。


您也可以使用mpl::map 方法:

using engine_map = mpl::map<
    mpl::pair<uint32_t, std::mt19937>,
    mpl::pair<uint64_t, std::mt19937_64>
>;

template <class Int>
using engine_t = typename mpl::at<engine_map, Int>::type;

这可以更好地扩展到更多类型。如果您引入一种新类型,这也将是一个硬编译错误,这可能是一种更好的方法。


我认为这是否比特征类更好或更差是一个偏好问题,并且取决于您项目的其余部分。

【讨论】:

  • 我不知道 std::conditional,它与本地类型别名 (using) 结合使用听起来很不错。 mpl::map 值得了解,但在这里可能有点矫枉过正。
  • @davidbak 这是我想到的第一件事,但在另外考虑mpl::map 和 Yakk 的两个解决方案时似乎是最糟糕的选择。
  • 哎呀,我看到你也有一个别名模板,我什至没有注意到。我猜,当你理解它的意思并且它看起来很自然时,我猜这是良好语法的标志......
  • 感谢您的回答 std::conditional,但我给 Yakk 打了勾,因为他回答中的标签系统很甜蜜。
猜你喜欢
  • 1970-01-01
  • 2020-08-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-11
  • 2012-11-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多