【问题标题】:Delayed evaluation of template type function模板类型函数的延迟评估
【发布时间】:2013-12-31 04:39:12
【问题描述】:

我阅读了“The C++ Programming language 第 4 版,第 1 次印刷,Bjarne Stroustrup 所著”一书(来自 Amazon.com)。 第 785 页。 Stroustrup 正在解释他如何在使用“std::conditional + std::make_unsigned”时消除“::type”的显式书写,使用“type aliases”(关键字“using”)。但是在“std::conditional + std::make_unsigned”上使用“类型别名”会导致编译错误。 到目前为止,一切都应该如此。他接着展示了如何使用“模板类型函数的延迟评估”来消除这些编译错误。

问题在线Atype<make_unsigned<string>myType2<string> ...

我使用的是 g++ 4.8.2。

编译:

g++ -std=c++1y test45.cpp -o a

#include <type_traits>
#include <string>
#include <iostream>
#include <typeinfo>  // for typeid(...)
using namespace std;

template<class T>
struct ErrIndicator {
   typedef ErrIndicator<T> type;
};

template<bool C, class T, class F>
using Conditional = typename conditional<C,T,F>::type;

template<typename T>
using Make_unsigned = typename make_unsigned<T>::type;

template<template<typename ...> class F, typename... Args>
using Delay = F<Args ...>;

template<class T>
using myType1 = Conditional<is_integral<T>::value,
    Make_unsigned<T>,
    ErrIndicator<T>
    >;

template<class T> 
    using myType2 = Conditional<is_integral<T>::value,
    Delay<Make_unsigned, T>,   // delayed evaluation
    ErrIndicator<T>
    >;

template<class T>
    using myType4 = Conditional<is_integral<T>::value,
    make_unsigned<T>,
    ErrIndicator<T>
    >;

template<typename T>
class Atype {}; 

template<typename T>
void func1(T &ia /* output param */) {
  cout << "unsigned integral type" << endl;
  ia = 4;  // "unsigned integral type" computation
}

template<typename T>
void func1(ErrIndicator<T> &) {
  cout << "non integral type: " << typeid(T).name() << endl;
}

int main() {
  myType1<int> var1a; // OK
  // myType1<string> var1b; // Error; The book says error
  //                    // should occur here. Here I understand.
  myType2<int> var2a; // OK
  // myType2<string> var2b; // Error - why?. Maybe I didn't get it,
  //                      // but I understand the book as no
  //                      // error should occur here.
  //                      // @DyP answered it.
  Atype<make_unsigned<string> > var3;  // OK here, look below at @DyP
  //                             // for "foo, bar, X" why
  //                        // make_unsigned<string> is not an error here.
  // make_unsigned<string> var6; // Error
  // Atype<make_unsigned<string>::type > var4;  // Error
  Atype<make_unsigned<int>::type > var5;  // OK
  //-------------
  myType4<string>::type var7;    // Look below for "myType3", where @Yakk
  //                          // obviates the necessity to write "::type".
  // rsl7 = 1:
  cout << "rsl7 = " << is_same<decltype(var7), ErrIndicator<string> >::value << endl; 
  func1(var7);  // "non integral type" overload of func1()
  //---------
  myType4<int>::type var8;
  // rsl8 = 1:
  cout << "rsl8 = " << is_same<decltype(var8), unsigned int>::value << endl; 
  func1(var8);  // "unsigned integral type" overload of func1()
}

【问题讨论】:

  • 字符串应该的错误是一个错误。 make_unsigned 要求其模板参数是整数,但不是 bool 类型。据我了解,不使用嵌套的type 并不在意。
  • 你认为Delay 在做什么?书中的哪句话让你这么想?我的意思是,延迟评估可能会有所帮助,但是是什么让您认为 Delay 会这样做?
  • @Yakk 我认为它应该延迟对元函数Make_unsigned 的评估,直到检查完类型是否为整数。
  • @DyP 当然,但除了它的名字,它为什么要这样做?
  • @Yakk Stroustrup 介绍了一个 conditional&lt; is_integral&lt;T&gt;::value, make_unsigned&lt;T&gt;, Error&lt;T&gt; &gt;::type 并谈到使用它而不是 typename make_unsigned&lt;T&gt;::type 可以防止编译时错误。

标签: c++ templates c++11


【解决方案1】:

我认为 Stroustrup 打算延迟对make_unsigned&lt;T&gt;::type 的访问,因为这种嵌套类型不是为非整数类型定义的。但是,对于 clang++ 和 g++ 来说,使用别名模板似乎是不够的:它们将 Delay&lt;Make_unsigned,T&gt; 直接解析为 Make_unsigned&lt;T&gt;,而 this 解析为 make_unsigned&lt;T&gt;::type

整个例子是:

template<typename C, typename T, typename F>
using Conditional = typename std::conditional<C,T,F>::type;

template<typename T>
using Make_unsigned = typename std::make_unsigned<T>::type;

// the example
Conditional<
  is_integral<T>::value,
  Delay<Make_unsigned,T>,
  Error<T>
>

// "The implementation of a perfect `Delay` function is nontrivial,
//  but for many uses this will do:"
template<template<typename...> class F, typename... Args>
using Delay = F<Args...>;

问题当然是,Delay&lt;Make_Unsigned,T&gt; 什么时候解决?对于类模板(不是别名模板),它们仅在需要完整的对象类型或程序的语义受到影响时才会隐式实例化。考虑:

#include <type_traits>
using namespace std;

template<class T>
struct foo
{
    static_assert(is_same<T, void>{}, "!");
};

template<class X>
struct bar
{
    // without the line below, no error!
    //X x;
};

int main()
{
    bar<foo<int>> b;
}

但是,别名模板并非如此。它们被替换为 [temp.alias]/2

当一个template-id指代一个别名模板的特化时,它等价于关联类型 通过将其 template-arguments 替换为别名的 type-id 中的 template-parameters 获得 模板。

恕我直言,这表明在上面的示例中,Delay&lt;Make_unsigned,T&gt; 等同于 make_unsigned&lt;T&gt;::type实例化 make_unsigned&lt;string&gt;::type 并导致编译时错误。

【讨论】:

  • 这意味着您不能编写这样的Delay 元函数:您必须使用类模板,并且需要嵌套 typedef type(或继承,这会导致其他问题) .所以你可以写一个delay(没有任何目的),但不能写一个Delay
  • 那么 Stroustrup 的 Delay 到底是什么意思?延迟有什么用?书中的整段都是关于这个延迟的事情。
  • @vlakov 我不认为整段都是关于Delay。这是关于无法使用Make_unsigned&lt;T&gt;,因为那将是不正确的。您必须在make_unsigned&lt;T&gt;Error&lt;T&gt; 之间进行选择,然后然后 访问结果的::type。然后他建议而不是这样做以延迟对make_unsigned&lt;T&gt;::type 的访问,但我认为这不能通过别名模板来实现。
  • @vlakov 我认为你可以使用 Yakk 建议的 conditional_apply 来代替 Delay,或者像 template&lt;bool C, class T, class F&gt; using Conditional_apply = typename conditional&lt;C,T,F&gt;::type::type; 这样使用 Conditional_apply&lt;is_integral&lt;T&gt;::value, make_unsigned&lt;T&gt;, Error&lt;T&gt;&gt;
  • 他写了关于隐藏::type的文章。他提供了 2 种选择。 1. 他明确使用::type 的替代方案编译没有错误。第二种选择是Delay。但这种选择失败了。所以Delay是书中的错误?
【解决方案2】:
template<bool, template<typename...>class Lhs, template<typename...>class Rhs, typename... Ts>
struct conditional_apply {
  typedef Lhs<Ts...> type;
};
template<template<typename...>class Lhs, template<typename...>class Rhs, typename...Ts>
struct conditional_apply<false, Lhs, Rhs, Ts...> {
  typedef Rhs<Ts...> type;
};
template<bool b, template<typename...>class Lhs, template<typename...>class Rhs, typename... Ts>
using Conditional_apply=typename conditional_apply<b, Lhs, Rhs, Ts...>::type;

template<class T> using myType3 = Conditional_apply<is_integral<T>::value, Make_unsigned, ErrIndicator, T >;

应该在T 上实际延迟应用templates,除非有错别字(在电话上)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-06-01
    • 2016-08-14
    • 2021-05-17
    • 1970-01-01
    • 1970-01-01
    • 2019-06-19
    • 2017-02-15
    相关资源
    最近更新 更多