C++ 中的自动模板实例化?

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

我现在工作的代码库已经过大量优化。本着这种精神,函数通常采用

bool
enum
模板参数,然后使用
if constexpr
在它们上进行分支。关键是,我们可以确保在处理数百万个项目的紧密循环中,该函数实际上只包含实际需要的代码。例如:

template<bool use_filter> void foo(Items& items) {
  if constexpr (use_filter) {
     if (!filter(items)) return;
  }
  process(items);
}

显然,实际功能要复杂得多,但上面的示例演示了如何使用它。问题是,现在我们的代码如下所示:

  if (query.use_filter()) {
    foo<true>(items);
  } else {
    foo<false>(items);
  }

实际上更糟糕,因为我们经常有多个模板参数,因此 if 森林变得非常大。有趣的是我真正想写的是

foo<query.use_filter()>(items)
。也就是说,以某种方式告诉 C++ 通过在后台自动实现分支来将运行时值转换为模板参数。是的,这是代码爆炸,但我们已经在这样做了。可以使用宏来实现这一点(在某种程度上),但它很快就会变得非常难看。最好不要使用宏。

问题:如果没有宏,这可能吗?我知道,用宏是可行的。

c++
3个回答
1
投票

将运行时值转换为编译时值的一种方法是使用

std::variant
std::visit
:

std::variant<std::true_type, std::false_type> as_bool_type(bool b)
{
    if (b) {
        return std::true_type{};
    } else {
        return std::false_type{};
    }
}

然后

std::visit([&](auto b/* possibly other param */){ foo<b>(items) },
           as_bool_type(query.use_filter()) /* possibly other variant..*/);

0
投票

您可以为值创建类似

std::visit
的内容。我称之为“编译时提升”,它在 Boost.Mp11 中为
mp_with_index
(用于
std::size_t
,但您可以轻松地与
bool
一起使用它):

// Boost version
mp_with_index<2>(query.use_filter(), [&](auto use_filter) {
    foo(items);
});

// No boost version
template<typename F>
constexpr decltype(auto) constexpr_promote(bool b, F&& f) {
    if (b)
        return std::forward<F>(f)(std::true_type{});
    return std::forward<F>(f)(std::false_type{});
}

constexpr_promote(query.use_filter(), [&](auto use_filter) {
    foo(items);
});

0
投票

当然!不过,老实说,如果您稍微重写一下模板函数,会更容易。

template<bool b>
using bool_constant = std::integral_constant<bool, b>

template<bool use_filter>
void foo(bool_constant<use_filter>, std::Items& items)

这有助于更直接地调用

foo
,而不必将参数作为模板和非模板参数传递。

然后我们写这个玩具:

using kbool = std::variant< bool_constant<false>, bool_constant<true> >;

a

kbool
有两种可能的状态,对应于
false
true

kbool evaluate( bool b ) {
  if (b) return bool_constant<true>{};
  else return bool_constant<false>{};
}

std::visit( [&](auto kb) {
  foo( kb, items );
}, evaluate(query.use_filter()) );

我们将运行时 bool 转换为 false/true 上的 std::variant,然后使用

foo
将其传递给
std::visit

你可以随心所欲地装饰它。

底层类型是

bool
是可选的;它可以是任何有限枚举(一定范围内的整数或枚举)。

运行时的

kbool
实际上是一个整数,如果为 false,则值为
0
;如果为 true,则值为
1
。我们只是将其打扮成可以让我们做类似
std::visit
之类的事情。

您甚至可以将

foo
包裹起来,这样它就需要一个
kbool
并在内部执行
std::visit
;这个包装器甚至可以直接调度
bool_constant
而无需
std::visit
ing。

然后

foo(query.use_filter(), items);

成为使用点的使用方式。

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