C ++模板元编程:使用类型别名和继承的不同行为 [英] C++ Template Meta Programming: Different Behavior using Types Aliases vs Inheritance

查看:195
本文介绍了C ++模板元编程:使用类型别名和继承的不同行为的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图逆转一个c ++ 14 std :: index_sequence ,并遇到了使用继承的原始实现的问题。我发现了一个使用本地类型别名的解决方法,但我想了解为什么原始代码不起作用。



/ p>

这是我第一次尝试取消 std :: index_sequence

  ///将元素附加到index_sequence的辅助类。 
///基本案例。
template< size_t,typename>
struct Append:std :: index_sequence<> {};

模板< size_t X,size_t ... XS>
struct Append< X,std :: index_sequence< XS ...>> :std :: index_sequence< XS ...,X> {};

/// std :: index_sequence的反向元素
模板< typename>
struct Reverse;

///基础案例
模板<>
struct Reverse< std :: index_sequence<>> :std :: index_sequence<> {};

template< size_t X,size_t ... XS>
struct Reverse< std :: index_sequence< X,XS ...>> :追加< X,Reverse< std :: index_sequence< XS ...>>>> {};

此支持函数打印 std :: index_sequence

 模板< size_t ... XS> 
void print_seq(std :: index_sequence< XS ...>)
{
std :: cout< size< sizeof ...(XS) :;
bool Do [] = {(std :: cout<<< XS<<,true)...}
(void)Do;
std :: cout<< std :: endl;
}

这个实现不能按预期工作:

  print_seq(Reverse< std :: make_index_sequence< 10>>> {}); 

输出的大小为0,没有元素:

  size 0:

反向使用类型别名



然后我稍微修改了我的原始代码,以使用类型别名而不是不一致。所有其他逻辑应该与第一个示例完全相同。

 模板< size_t,typename> 
struct AppendUsingType {
using type = std :: index_sequence<> ;;
};

template< size_t X,size_t ... XS>
struct AppendUsingType< X,std :: index_sequence< XS ...>> {
using type = std :: index_sequence< XS ...,X> ;
};

template< typename>
struct ReverseUsingType;

模板<>
struct ReverseUsingType< std :: index_sequence<>> {
using type = std :: index_sequence<> ;;
};

template< size_t X,size_t ... XS>
struct ReverseUsingType< std :: index_sequence< X,XS ...>> {
using type = typename AppendUsingType< X,typename ReverseUsingType< std :: index_sequence< XS ...>> :: type> :: type;
};

现在检查类型时:

  print_seq(typename ReverseUsingType< std :: make_index_sequence< 10>> :: type {}); 

我看到正确的输出:

  size 10:9 8 7 6 5 4 3 2 1 0 

问题



即使我找到一个解决方案,我真的想了解为什么使用继承的实现失败,而使用类型aliases行为如预期。我在gcc和Clang看到这一点,所以我怀疑在语言规范中有一些原因。



(可能相关问题: typedef vs public inheritance in c ++ meta-programming

解决方案

首先,部分特化的匹配是使用模板参数扣除来完成的。如果部分特化的模板参数可以从提供的参数中推导出来,则部分特化被认为是匹配并且可以使用。



对于函数调用,标准中的一个特殊规则允许从派生类推导出基类模板参数,如果推断会失败(§14.8.2.1[temp.deduct.call] / p4);此规则不适用于其他情况,特别是不适用于部分特化匹配。



因此,编译器不能匹配 Reverse< ; std :: index_sequence< XS ...>>< / code>与 std :: index_sequence< XS ...> 附加是不可行的,而是使用主模板。






不使用类型别名的可能的反转实施:

  template< typename,typename = std :: index_sequence< > 
struct Reverse;

template< size_t ... ints>
struct Reverse< std :: index_sequence<>,
std :: index_sequence< ints ...>> :std :: index_sequence< ints ...> {};

template< size_t first,size_t ... remaining,size_t ... done>
struct Reverse< std :: index_sequence< first,remaining ...>,std :: index_sequence< done ...>>
:Reverse< std :: index_sequence< remaining ...>,std :: index_sequence< first,done ...>> {};


I was trying to reverse a c++14 std::index_sequence and ran into problems with my original implementation that used inheritance. I found a workaround using local type aliases, but I would like to understand why the original code does not work.

Broken Reverse Using Inheritance

This was my first attempt at reversing a std::index_sequence:

/// Helper class that appends an element onto an index_sequence.
/// Base case.
template<size_t, typename>
struct Append : std::index_sequence<> { };

template<size_t X, size_t... XS>
struct Append<X, std::index_sequence<XS...>> : std::index_sequence<XS..., X> { };

/// Reverse elements of a std::index_sequence
template<typename>
struct Reverse;

/// Base case
template<>
struct Reverse<std::index_sequence<>> : std::index_sequence<> { };

template<size_t X, size_t... XS>
struct Reverse<std::index_sequence<X, XS...>> : Append<X, Reverse<std::index_sequence<XS...>>> { };

This support function prints the contents of an std::index_sequence:

template <size_t... XS>
void print_seq(std::index_sequence<XS...>)
{
    std::cout << "size " << sizeof...(XS) << ": ";
    bool Do[] = { (std::cout << XS << " ", true)... };
    (void) Do;
    std::cout << std::endl;
}

This implementation unfortunately did not work as I expected:

print_seq(Reverse<std::make_index_sequence<10>>{});

The output shows a size of 0 with no elements:

size 0: 

Working Reverse Using Type Aliases

Then I slightly revised my original code to use type aliases instead of inherence. All the other logic should be exactly the same as the first example.

template<size_t, typename>
struct AppendUsingType {
    using type = std::index_sequence<>;
};

template<size_t X, size_t... XS>
struct AppendUsingType<X, std::index_sequence<XS...>> {
    using type = std::index_sequence<XS..., X> ;
};

template<typename>
struct ReverseUsingType;

template<>
struct ReverseUsingType<std::index_sequence<>> {
    using type = std::index_sequence<>;
};

template<size_t X, size_t... XS>
struct ReverseUsingType<std::index_sequence<X, XS...>> {
    using type = typename AppendUsingType<X, typename ReverseUsingType<std::index_sequence<XS...>>::type>::type;
};

Now when I inspect the type:

print_seq(typename ReverseUsingType<std::make_index_sequence<10>>::type{});

I see the correct output:

 size 10: 9 8 7 6 5 4 3 2 1 0 

Question

Even though I found a solution, I would really like to understand why the implementation using inheritance fails while the one using type aliases behaves as expected. I see this in both gcc and Clang, so I suspect there some reason in the language specification.

(Perhaps Related Question: typedef vs public inheritance in c++ meta-programming)

解决方案

First, matching of partial specializations are done using template argument deduction. If the template parameters of the partial specialization can be deduced from the the arguments supplied, the partial specialization is considered a match and can be used.

For function calls, there is a special rule in the standard permitting deducing base class template parameters from a derived class if deduction would otherwise fail (§14.8.2.1 [temp.deduct.call]/p4); this rule does not apply in other situations, and in particular it doesn't apply to partial specialization matching.

Hence, the compiler cannot match Reverse<std::index_sequence<XS...>> against std::index_sequence<XS...>, the partial specialization of Append is not viable, and the primary template is used instead.


A possible implementation of reversal using no type aliases:

template<typename, typename=std::index_sequence<>>
struct Reverse;

template<size_t...ints>
struct Reverse<std::index_sequence<>,
               std::index_sequence<ints...>> : std::index_sequence<ints...>{};

template<size_t first, size_t...remaining, size_t...done>
struct Reverse<std::index_sequence<first, remaining...>, std::index_sequence<done...>>
    : Reverse<std::index_sequence<remaining...>, std::index_sequence<first, done...>> {};

这篇关于C ++模板元编程:使用类型别名和继承的不同行为的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆