【问题标题】:Inheriting from standard library type traits从标准库类型特征继承
【发布时间】:2017-04-19 10:12:49
【问题描述】:

在编写自定义类型特征时,我经常从标准库类型特征中派生出它们,如下所示:

template<typename T>
struct some_type_trait:
    std::is_arithmetic<T>
{};

但是,我有时想知道从标准库类型特征的type 成员类型继承是否更干净:

template<typename T>
struct some_type_trait:
    std::is_arithmetic<T>::type
{};

总体思路是最终只有从std::bool_constant 的继承很重要,但事实上我们在第一个示例中继承自std::is_arithmetic 而不是直接从std::bool_constant 继承(如在第二个示例中)可通过多态性或 std::is_base_of 等实用程序观察到。

要点是通过type 成员类型直接从bool_constant 继承感觉更简洁,因为它正是我们想要的。但是,从std::is_arithmetic 继承会稍微短一些,并且提供基本相同的行为。那么......在选择一个或另一个(正确性,编译时间......)时我可能会错过任何微妙的优势吗?与直接从底层 bool_constant 继承相比,是否存在从 std::is_arithmetic 继承可能会改变应用程序行为的微妙场景?

【问题讨论】:

  • 那是一些高质量的自行车:D
  • FWIW,en.cppreference.com 上的“可能实现”直接继承std::integral_constant&lt;bool, some_check&gt;。遵循这种“风格”,您的示例将继承自 std::integral_constant&lt;bool, std::is_arithmetic&lt;T&gt;::value&gt;
  • @DanielJour 如果我没记错的话,从std::integral_constant 继承对于类型特征是强制性的。问题是通过::type 直接从std::integral_constant 派生还是通过类型特征本身间接派生更好。也许我不明白你的评论:/
  • 虽然我对直接从类型特征继承与使用 type 成员类型相比是否存在不利/可能的陷阱非常感兴趣。
  • 我认为您需要从 std::is_arithmetic&lt;T&gt; 继承才能选择对特征进行惰性评估。拥有它很有用。如果你想要一个“干净”的界面,你也可以使用template&lt;class T&gt; using some_type_trait_t = some_type_trait&lt;T&gt;::type

标签: c++ c++11 typetraits


【解决方案1】:

第二个有泄漏实现细节的小缺陷;如果您从is_arithmetic&lt;int&gt; 或其他继承,有人可以通过函数重载进行测试。这可能会“起作用”并导致误报。

这是一个非常小的问题。

如果您的编译器转储基类名称,您可能会通过从 is_arithmetic 继承获得更好的诊断。

你的设计都不是很好。而是:

template<class T>
struct some_type_trait:
  std::integral_constant<bool,
    std::is_arithmetic<T>{}
  >
{};

可以扩展,因为我们可以在其中放置任何表达式。

正如我之前提到的,在错误消息中包含类型可能会有所帮助,因此我们可以这样做:

constexpr bool all_of() { return true; }
template<class...Bools>
constexpr bool all_of(bool b0, Bools...bs) {
    return b0 && all_of(bs...);
}
template<class T, template<class...>class...Requirements>
struct Requires : std::integral_constant<bool,
  Requirements<T>{} &&...
  // in C++11/14, something like: all_of(Requirements<T>::value...)
> {};

然后我们得到:

template<class T>
using some_type_trait = Requires<T, std::is_arithmetic>;

如果它在标签调度中找不到重载,将产生一个错误,可能会给你一个线索。

template<class T>
void test( std::true_type passes_test, T t ) {
  std::cout << t+0 << "\n";
}
template<class T>
void test(T t) {
  return test(some_type_trait<T>{}, t);
}
int main() {
  test(3);
  test("hello");
}

遗憾的是,我们在模板元编程中没有简单绑定/部分应用程序/currying 的等价物。所以f&lt;.&gt; = is_base_of&lt;X, .&gt;很难简洁表达。

Live example,我们得到的错误信息是: 铿锵声:

main.cpp:23:10: note: candidate function [with T = const char *] not viable: no known conversion from 'some_type_trait<const char *>' (aka 'Requires<const char *, std::is_arithmetic>') to 'std::true_type' (aka 'integral_constant<bool, true>') for 1st argument

gcc:

main.cpp:28:18: note:   cannot convert 'Requires<const char*, std::is_arithmetic>()' (type 'Requires<const char*, std::is_arithmetic>') to type 'std::true_type {aka std::integral_constant<bool, true>}'

这至少会导致您出错(const char* 不是is_arithmetic)。

【讨论】:

  • 编译器不会假定它是该上下文中的值,因为它不能是值。所以从 is_arithmetic&lt;T&gt;::type 继承就可以了。没必要,但很好。
  • @Barry 为我提供不编译的权利。删除幽默前缀。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-06-02
  • 1970-01-01
  • 1970-01-01
  • 2020-11-11
相关资源
最近更新 更多