gpt4 book ai didi

c++ - 懒惰评估(短路)模板条件类型的通用方法

转载 作者:IT老高 更新时间:2023-10-28 22:39:46 29 4
gpt4 key购买 nike

在玩弄编译时字符串(char 的可变参数列表)操作时,我需要实现一种方法来检查编译时字符串是否包含另一个(较小的)编译时字符串。

这是我的第一次尝试:

template<int I1, int I2, typename, typename> struct Contains;

template<int I1, int I2, char... Cs1, char... Cs2>
struct Contains<I1, I2, CharList<Cs1...>, CharList<Cs2...>>
{
using L1 = CharList<Cs1...>;
using L2 = CharList<Cs2...>;
static constexpr int sz1{L1::size};
static constexpr int sz2{L2::size};

using Type = std::conditional
<
(I1 >= sz1),
std::false_type,
std::conditional
<
(L1::template at<I1>() != L2::template at<I2>()),
typename Contains<I1 + 1, 0, L1, L2>::Type,
std::conditional
<
(I2 == sz2 - 1),
std::true_type,
typename Contains<I1 + 1, I2 + 1, L1, L2>::Type
>
>
>;
};

我发现这个解决方案非常容易阅读和推理。不幸的是,它不起作用

编译器总是试图实例化 std::conditional 的每一个分支,即使是那些没有被采取的。换句话说,短路并没有发生。

这会导致 Contains被无限实例化。

我通过分隔每个 std::conditional 解决了我最初的问题 block 在一个单独的模板类中,条件结果作为部分特化处理。

它有效,但不幸的是我发现它很难阅读/修改。


有没有办法懒惰地实例化模板类型并接近我原来的解决方案?

这是代码的示例:

using Type = std::conditional
<
(I1 >= sz1),
std::false_type,
std::conditional
<
(L1::template at<I1>() != L2::template at<I2>()),
DeferInstantiation<typename Contains<I1 + 1, 0, L1, L2>::Type>,
std::conditional
<
(I2 == sz2 - 1),
std::true_type,
DeferInstantiation<typename Contains<I1 + 1, I2 + 1, L1, L2>::Type>
>
>
>;

有没有可能实现DeferInstantiation<T> ?

最佳答案

The compiler always tries to instantiate every single branch of std::conditional, even those which are not taken. To put it in another way, short-circuiting isn't happening.

std::conditional<B,T,F>提供用于执行编译时给定类型之间的选择TF ,取决于 bool 值 B .这选择受特化的影响。当B为真,实例化的特化是:

std::conditional<true,T,F>
{
typedef T type;
};

B为假,实例化的特化是:

std::conditional<false,T,F>
{
typedef F type;
};

请注意,要实例化 任何一个 特化,TF必须被实例化。 没有“分支”。 “短路”的概念std::conditional<true,T,F> 的任何一个的实例化或 std::conditional<false,T,F>只能表示不这样做

所以不,不可能实现 DeferInstantiation<U> , 对于类型参数 U , 这样

std::conditional<{true|false},DeferInstantiation<T>,DeferInstantiation<F>>

不需要实例化 DeferInstantiation<T>DeferInstantiation<F>> ,因此 T , 和 F .

用于执行编译时选择哪个或两个或多个模板实例化后,该语言提供特化(如图所示根据 std::conditional<B,T,F> 的定义本身);它提供函数模板重载分辨率,并提供SFINAE .特化和重载解决方案都可以协同利用SFINAE,通过 std::enable_if<B,T> 的库支持

阻碍您制作特定递归元函数的问题您想要的不是在给定的 types 之间进行选择,而是选择 template递归实例化应指向其中。 std::conditional不是达到目的。 @Pradhan 的回答表明模板不同于 std::conditional可以很好地编写以在两个 模板 之间进行编译时选择,而无需意味着它们都应该被实例化。他运用特化来做到这一点。

正如你所说,你已经想出了一个特化的解决方案问题。这原则上是递归控制的正确方法递归元函数中的模板选择。然而,随着 constexpr , 递归元函数并不像他们以前做过的问题,以及他们引起的大部分脑痛已成为过去。

这里的特殊问题 - 在编译时确定一个字符串是否是子字符串另一个 - 可以在不使用模板元编程的情况下解决,并且无需表示编译时字符串,而不是传统的字符串文字:

#include <cstddef>

constexpr std::size_t str_len(char const *s)
{
return *s ? 1 + str_len(s + 1) : 0;
}

constexpr bool
is_substr(char const * src, char const *targ,
std::size_t si = 0, std::size_t ti = 0)
{
return !targ[ti] ? true :
str_len(src + si) < str_len(targ + ti) ? false :
src[si] == targ[ti] ?
is_substr(src,targ,si + 1, ti + 1) :
is_substr(src,targ,si + 1, 0);
}

// Compiletime tests...

static_assert(is_substr("",""),"");
static_assert(is_substr("qwerty",""),"");
static_assert(is_substr("qwerty","qwerty"),"");
static_assert(is_substr("qwerty","qwert"),"");
static_assert(is_substr("qwerty","werty"),"");
static_assert(is_substr("qwerty","wert"),"");
static_assert(is_substr("qwerty","er"),"");
static_assert(!is_substr("qwerty","qy"),"");
static_assert(!is_substr("qwerty","et"),"");
static_assert(!is_substr("qwerty","qwertyz"),"");
static_assert(!is_substr("qwerty","pqwerty"),"");
static_assert(!is_substr("","qwerty"),"");

int main()
{
return 0;
}

这将编译为 C++11 或更高版本。

您可能有理由希望表示编译时字符串作为 CharList<char ...>除了因此使他们服从诸如此类的 TMP 编译时查询。我们可以看到 CharList<char ...Cs>有一个静态常数 size成员(member)评价为sizeof...(Cs)并且有静态at<N>()评估为 N 的成员函数第...Cs .在这种情况下(假设 at<N>() 已调试),您可能会适应 is_substr是一个模板函数,期望 CharList<char ...>参数大致如下:

#include <type_traits>

template<
class SrcList, class TargList, std::size_t SrcI = 0, std::size_t TargI = 0>
constexpr typename
std::enable_if<(TargI == TargList::size && SrcI <= SrcList::size),bool>::type
is_substr()
{
return true;
}

template<
class SrcList, class TargList, std::size_t SrcI = 0, std::size_t TargI = 0>
constexpr typename
std::enable_if<(TargI < TargList::size && SrcI == SrcList::size),bool>::type
is_substr()
{
return false;
}

template<
class SrcList, class TargList, std::size_t SrcI = 0, std::size_t TargI = 0>
constexpr typename
std::enable_if<(TargI < TargList::size && SrcI < SrcList::size),bool>::type
is_substr()
{
return SrcList::template at<SrcI>() == TargList::template at<TargI>() ?
is_substr<SrcList,TargList,SrcI + 1,TargI + 1>() :
is_substr<SrcList,TargList,SrcI + 1,0>();
}

这说明了 SFINAE 的应用,由 std::enable_if 使用

最后,你也可以对这个程序感兴趣:

#include <iostream>

template<char const * Arr>
struct string_lit_type
{
static constexpr const char * str = Arr;
static constexpr std::size_t size = str_len(str);
static constexpr char at(std::size_t i) {
return str[i];
}
};

constexpr char arr[] = "Hello World\n";

int main()
{
std::cout << string_lit_type<arr>::str;
std::cout << string_lit_type<arr>::size << std::endl;
std::cout << string_lit_type<arr>::at(0) << std::endl;
return 0;
}

哪个打印:

Hello World
12
H

(使用g++ 4.9、clang 3.5编译的代码)

关于c++ - 懒惰评估(短路)模板条件类型的通用方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28432977/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com